Os softwares são executados no sistema operacional em uma premissa muito simples - eles requerem memória. O sistema operacional do dispositivo fornece isso na forma de RAM. A quantidade de memória necessária pode variar - alguns softwares precisam de muita memória, outros requerem memória insignificante. A maioria (senão todos) dos usuários executa vários aplicativos no sistema operacional simultaneamente e, como a memória é cara (e o tamanho do dispositivo é finito), a quantidade de memória disponível é sempre limitada. Assim, dado que todos os softwares requerem uma certa quantidade de RAM, e todos podem ser executados ao mesmo tempo, o sistema operacional tem que cuidar de duas coisas:
- Que o software sempre seja executado até que o usuário o aborte, ou seja, ele não deve abortar automaticamente porque o sistema operacional ficou sem memória.
- A atividade acima, mantendo um desempenho respeitável para os softwares em execução.
Agora a questão principal se resume a como a memória está sendo gerenciada. O que exatamente governa onde os dados pertencentes a um determinado software residirão na memória?
Solução possível 1 : permitir que softwares individuais especifiquem explicitamente o endereço de memória que usarão no dispositivo. Suponha que o Photoshop declare que sempre usará endereços de memória variando de 0
a 1023
(imagine a memória como um array linear de bytes, então o primeiro byte está no local 0
, o 1024
byte está no local 1023
) - isto é, ocupando 1 GB
memória. Da mesma forma, o VLC declara que ocupará intervalo de memória 1244
para 1876
, etc.
Vantagens:
- Cada aplicativo é pré-atribuído a um slot de memória, então quando é instalado e executado, ele apenas armazena seus dados naquela área de memória e tudo funciona bem.
Desvantagens:
Isso não escala. Teoricamente, um aplicativo pode exigir uma grande quantidade de memória quando está fazendo algo realmente pesado. Portanto, para garantir que nunca fique sem memória, a área de memória alocada a ele deve ser sempre maior ou igual a essa quantidade de memória. E se um software, cujo uso máximo teórico de memória é 2 GB
(portanto, exigindo 2 GB
alocação de memória da RAM), for instalado em uma máquina com apenas 1 GB
memória? O software deve simplesmente abortar na inicialização, informando que a RAM disponível é menor que 2 GB
? Ou deve continuar e, no momento em que a memória necessária ultrapassar 2 GB
, apenas abortar e sair com a mensagem de que não há memória suficiente disponível?
Não é possível evitar a mutilação da memória. Existem milhões de softwares por aí, mesmo que cada um deles tivesse apenas 1 kB
memória, o total de memória necessária seria excedido 16 GB
, o que é mais do que a maioria dos dispositivos oferece. Como podem, então, diferentes softwares receber slots de memória que não invadam as áreas uns dos outros? Em primeiro lugar, não existe um mercado centralizado de software que possa regular que quando um novo software está sendo lançado, ele deve atribuir a si mesmo essa quantidade de memória desta área ainda não ocupadae, em segundo lugar, mesmo que houvesse, não é possível porque o não. de softwares é praticamente infinito (exigindo memória infinita para acomodar todos eles), e a RAM total disponível em qualquer dispositivo não é suficiente para acomodar nem mesmo uma fração do que é necessário, tornando inevitável a invasão dos limites de memória de um software sobre o de outro. Então, o que acontece quando Photoshop é atribuído posições de memória 1
para 1023
e VLC é atribuído 1000
a 1676
? E se o Photoshop armazenar alguns dados no local 1008
, o VLC sobrescrever isso com seus próprios dados e, posteriormente, o Photoshopacessa pensando que são os mesmos dados que estavam armazenados lá anteriormente? Como você pode imaginar, coisas ruins acontecerão.
Então, claramente, como você pode ver, essa ideia é um tanto ingênua.
Possível solução 2 : vamos tentar outro esquema - onde o SO fará a maior parte do gerenciamento de memória. Os softwares, sempre que necessitarem de alguma memória, apenas solicitarão o SO, e o SO se acomodará de acordo. Say OS garante que sempre que um novo processo está solicitando memória, ele alocará a memória do endereço de byte mais baixo possível (como disse anteriormente, a RAM pode ser imaginada como uma matriz linear de bytes, portanto, para uma 4 GB
RAM, o intervalo de endereços para um byte de 0
para2^32-1
) se o processo estiver iniciando, caso contrário, se for um processo em execução solicitando a memória, ele alocará a partir do último local de memória onde esse processo ainda reside. Uma vez que os softwares estarão emitindo endereços sem considerar qual será o endereço real da memória onde os dados são armazenados, o sistema operacional terá que manter um mapeamento, por software, do endereço emitido pelo software para o endereço físico real (Nota: essa é uma das duas razões pelas quais chamamos esse conceito Virtual Memory
. Os softwares não se importam com o endereço de memória real onde seus dados estão sendo armazenados, eles apenas cuspem os endereços na hora, e o sistema operacional encontra o lugar certo para encaixá-los e encontrá-los mais tarde, se necessário).
Digamos que o dispositivo acabou de ser ligado, o sistema operacional acabou de iniciar, no momento não há nenhum outro processo em execução (ignorando o sistema operacional, que também é um processo!) E você decide iniciar o VLC . Portanto, o VLC recebe uma parte da RAM dos endereços de byte mais baixo. Boa. Agora, enquanto o vídeo está sendo executado, você precisa iniciar seu navegador para visualizar alguma página da web. Em seguida, você precisa iniciar o Bloco de notas para rabiscar algum texto. E então Eclipse para fazer alguma codificação .. Logo sua memória 4 GB
é toda usada, e a RAM fica assim:
Problema 1: Agora você não pode iniciar nenhum outro processo, pois toda a RAM está esgotada. Assim, os programas devem ser escritos tendo em mente o máximo de memória disponível (praticamente ainda menos estará disponível, já que outros softwares também estarão rodando paralelamente!). Em outras palavras, você não pode executar um aplicativo que consome muita memória em seu 1 GB
PC decrépito .
Ok, agora que você decidiu que não precisa mais manter o Eclipse e o Chrome abertos, feche-os para liberar memória. O espaço ocupado na RAM por esses processos é recuperado pelo SO e se parece com isto agora:
Suponha que fechar esses dois libera 700 MB
espaço - ( 400
+ 300
) MB. Agora você precisa iniciar o Opera , que ocupará 450 MB
espaço. Bem, você tem mais do que 450 MB
espaço disponível no total, mas ... não é contíguo, é dividido em pedaços individuais, nenhum dos quais é grande o suficiente para caber 450 MB
. Então você teve uma ideia brilhante, vamos mover todos os processos abaixo para o mais alto possível, o que deixará o 700 MB
espaço vazio em um pedaço na parte inferior. Isso é chamadocompaction
. Ótimo, exceto que ... todos os processos que existem estão em execução. Movê-los significará mover o endereço de todo o seu conteúdo (lembre-se, o SO mantém um mapeamento da memória cuspida pelo software para o endereço de memória real. Imagine que o software cuspiu um endereço de 45
com dados 123
e o SO o armazenou no local 2012
e criou uma entrada no mapa, mapeando 45
para 2012
. Se o software agora for movido na memória, o que costumava estar no local 2012
não estará mais 2012
, mas em um novo local, e o sistema operacional precisa atualizar o mapa de acordo para mapear 45
para o novo endereço, para que o software possa obter os dados esperados ( 123
) ao consultar a localização da memória 45
. No que diz respeito ao software, tudo o que ele conhece é esse endereço45
contém os dados 123
!)! Imagine um processo que faz referência a uma variável local i
. No momento em que for acessado novamente, seu endereço mudou e ele não poderá mais encontrá-lo. O mesmo vale para todas as funções, objetos, variáveis, basicamente tudo tem um endereço, e mover um processo significará mudar o endereço de todos eles. O que nos leva a:
Problema 2: Você não pode mover um processo. Os valores de todas as variáveis, funções e objetos dentro desse processo têm valores codificados como cuspidos pelo compilador durante a compilação, o processo depende deles estarem no mesmo local durante seu tempo de vida e alterá-los é caro. Como resultado, os processos deixam um grande " holes
" para trás quando são encerrados. Isso é chamado
External Fragmentation
.
Bem. Suponha que de alguma forma, de alguma maneira milagrosa, você consiga mover os processos para cima. Agora há 700 MB
espaço livre na parte inferior:
O Opera se encaixa perfeitamente na parte inferior. Agora sua RAM se parece com isto:
Boa. Tudo parece bem. No entanto, não há muito espaço disponível e agora você precisa iniciar o Chrome novamente, um conhecido devorador de memória! Ele precisa de muita memória para iniciar e você quase não tem memória sobrando ... Exceto ... você agora percebe que alguns dos processos, que inicialmente ocupavam um grande espaço, agora não estão precisando de muito espaço. Pode ser que você tenha interrompido seu vídeo no VLC , portanto, ele ainda está ocupando algum espaço, mas não tanto quanto o necessário durante a execução de um vídeo de alta resolução. Da mesma forma para o bloco de notas e fotos . Sua RAM agora se parece com isto:
Holes
, de novo! De volta à estaca zero! Exceto, anteriormente, os buracos ocorriam devido ao encerramento de processos, agora são devido a processos que exigiam menos espaço do que antes! E você novamente tem o mesmo problema, os holes
combinados rendem mais espaço do que o necessário, mas eles estão espalhados, não muito úteis isoladamente. Portanto, você precisa mover esses processos novamente, uma operação cara e muito frequente, já que os processos freqüentemente reduzem de tamanho ao longo de sua vida útil.
Problema 3: Os processos, ao longo de sua vida útil, podem reduzir de tamanho, deixando para trás espaço não utilizado, que, se necessário, exigirá a operação cara de mover muitos processos. Isso é chamado
Internal Fragmentation
.
Tudo bem, então agora seu sistema operacional faz o necessário, move os processos e inicia o Chrome e, depois de algum tempo, sua RAM fica assim:
Legal. Agora, suponha que você volte a assistir Avatar no VLC . Sua necessidade de memória vai disparar! Mas ... não há espaço para crescer, pois o bloco de notas está aninhado em sua parte inferior. Portanto, novamente, todos os processos devem ser movidos para baixo até que o VLC encontre espaço suficiente!
Problema 4: Se os processos precisarem crescer, será uma operação muito cara
Bem. Agora, suponha que o Fotos esteja sendo usado para carregar algumas fotos de um disco rígido externo. Acessar o disco rígido leva você do reino dos caches e RAM para o do disco, que é mais lento por ordens de grandeza. Dolorosamente, irrevogavelmente, transcendentalmente mais lento. É uma operação de E / S, o que significa que não está vinculada à CPU (é exatamente o oposto), o que significa que não precisa ocupar RAM agora. No entanto, ele ainda ocupa a RAM teimosamente. Se você quiser iniciar o Firefox nesse ínterim, não pode, porque não há muita memória disponível, ao passo que se o Fotos fosse tirado da memória durante sua atividade de I / O, ele teria liberado muita memória, seguido pela compactação (cara), seguida pelo encaixe do Firefox .
Problema 5: os trabalhos vinculados a E / S continuam ocupando RAM, levando à subutilização de RAM, que poderia ter sido usada por trabalhos vinculados à CPU nesse ínterim.
Então, como podemos ver, temos tantos problemas até com a abordagem da memória virtual.
Existem duas abordagens para lidar com esses problemas - paging
e segmentation
. Deixe-nos discutir paging
. Nessa abordagem, o espaço de endereço virtual de um processo é mapeado para a memória física em blocos - chamados pages
. Um page
tamanho típico é 4 kB
. O mapeamento é mantido por algo chamado de page table
, dado um endereço virtual, tudo o que agora temos que fazer é descobrir a que page
o endereço pertence, em seguida page table
, encontrar o local correspondente para aquele page
na memória física real (conhecido como frame
), e dado se o deslocamento do endereço virtual dentro do page
é o mesmo para o page
e também para o frame
, descubra o endereço real adicionando esse deslocamento ao endereço retornado pelo page table
. Por exemplo:
À esquerda está o espaço de endereço virtual de um processo. Digamos que o espaço de endereço virtual requer 40 unidades de memória. Se o espaço de endereço físico (à direita) também tivesse 40 unidades de memória, seria possível mapear todas as localizações da esquerda para uma localização à direita, e teríamos ficado muito felizes. Mas, por azar, não apenas a memória física tem menos (24 aqui) unidades de memória disponíveis, ela também deve ser compartilhada entre vários processos! Tudo bem, vamos ver como faremos com isso.
Quando o processo começa, digamos que uma solicitação de acesso à memória para localização 35
seja feita. Aqui, o tamanho da página é 8
(cada um page
contém 8
locais, todo o espaço de endereço virtual dos 40
locais, portanto, contém 5
páginas). Portanto, este local pertence à página no. 4
( 35/8
). Dentro disso page
, este local tem um deslocamento de 3
( 35%8
). Portanto, este local pode ser especificado pela tupla (pageIndex, offset)
= (4,3)
. Este é apenas o começo, então nenhuma parte do processo é armazenada na memória física real ainda. Assim page table
, o , que mantém um mapeamento das páginas à esquerda para as páginas reais à direita (onde são chamadasframes
) está vazio no momento. Assim, o sistema operacional abre mão da CPU, permite que um driver de dispositivo acesse o disco e busque a página nº. 4
para este processo (basicamente um pedaço de memória do programa no disco, cujos endereços variam de 32
a 39
). Quando chega, o sistema operacional aloca a página em algum lugar da RAM, digamos o próprio primeiro quadro, e o page table
para este processo toma nota de que a página 4
mapeia para o quadro 0
na RAM. Agora, os dados estão finalmente na memória física. O SO consulta novamente a tabela de páginas para a tupla (4,3)
e, desta vez, a tabela de páginas diz que a página 4
já está mapeada para o quadro 0
na RAM. Assim, o sistema operacional simplesmente vai para o 0
º quadro na RAM, acessa os dados no deslocamento 3
desse quadro (reserve um momento para entender isso.page
, que foi obtido do disco, é movido para frame
. Portanto, seja qual for o deslocamento de um local de memória individual em uma página, ele será o mesmo no quadro também, já que dentro de page
/ frame
, a unidade de memória ainda reside no mesmo lugar relativamente!) E retorna os dados! Como os dados não foram encontrados na memória na primeira consulta em si, mas tiveram que ser buscados do disco para serem carregados na memória, isso constitui um erro .
Bem. Agora suponha que 28
seja feito um acesso à memória para localização . Tudo se resume a (3,4)
. Page table
agora tem apenas uma entrada, mapeamento de página 4
para quadro 0
. Portanto, isso é novamente uma falha , o processo cede a CPU, o driver do dispositivo busca a página do disco, o processo recupera o controle da CPU novamente e ele page table
é atualizado. Digamos que agora a página 3
esteja mapeada para quadro 1
na RAM. Assim, (3,4)
torna-se (1,4)
, e os dados nesse local na RAM é retornado. Boa. Dessa forma, suponha que o próximo acesso à memória seja por localização 8
, que se traduz em (1,0)
. A página 1
ainda não está na memória, o mesmo procedimento é repetido e o page
está alocado no quadro2
na RAM. Agora, o mapeamento do processo de RAM se parece com a imagem acima. Neste momento, a RAM, que tinha apenas 24 unidades de memória disponível, está cheia. Suponha que a próxima solicitação de acesso à memória para este processo seja do endereço 30
. Ele mapeia para (3,6)
e page table
diz que a página 3
está na RAM e mapeia para o quadro 1
. Yay! Portanto, os dados são buscados no local da RAM (1,6)
e retornados. Isso constitui um sucesso , pois os dados necessários podem ser obtidos diretamente da RAM, sendo, portanto, muito rápidos. Da mesma forma, as próximas solicitações de acesso, digamos, por locais 11
, 32
, 26
, 27
todos são acertos , ou seja, dados solicitados pelo processo encontra-se diretamente na RAM sem a necessidade de procurar em outro lugar.
Agora, suponha que uma solicitação de acesso à memória para localização 3
venha. Isso se traduz em (0,3)
, e page table
para este processo, que atualmente tem 3 entradas, para páginas 1
, 3
e 4
diz que esta página não está na memória. Como nos casos anteriores, ele é obtido do disco; no entanto, ao contrário dos casos anteriores, a RAM está cheia! Então o que fazer agora? Aqui está a beleza da memória virtual, um quadro da RAM é despejado! (Vários fatores determinam qual quadro deve ser removido. Pode ser LRU
baseado, onde o quadro que foi acessado menos recentemente para um processo deve ser removido. Pode ser a first-come-first-evicted
base, onde o quadro que alocou há mais tempo, é removido, etc. .) Portanto, algum quadro é despejado. Diga o quadro 1 (apenas escolhendo aleatoriamente). No entanto, isso frame
é mapeado para algunspage
! (Atualmente, ele é mapeado pela tabela de página para página 3
de nosso único processo). Portanto, esse processo tem que ser informado desta notícia trágica, aquele frame
, que infelizmente pertence a você, é ser despejado da RAM para dar lugar a outro pages
. O processo deve garantir que ele se atualize page table
com essas informações, ou seja, removendo a entrada para aquela dupla de page-frame, para que na próxima vez que for feito um pedido para isso page
, ele diga corretamente ao processo que isso page
não está mais na memória , e deve ser obtido do disco. Boa. Assim, o quadro 1
é removido, a página 0
é trazida e colocada lá na RAM e a entrada da página 3
é removida e substituída pelo 0
mapeamento de página para o mesmo quadro1
. Portanto, agora nosso mapeamento se parece com isto (observe a mudança de cor no segundo frame
do lado direito):
Viu o que aconteceu? O processo precisava crescer, precisava de mais espaço do que a RAM disponível, mas, ao contrário de nosso cenário anterior, onde todos os processos na RAM precisavam ser movidos para acomodar um processo crescente, aqui aconteceu com apenas uma page
substituição! Isso foi possível pelo fato de que a memória de um processo não precisa mais ser contígua, pode residir em diferentes locais em blocos, o SO mantém as informações de onde eles estão e, quando necessário, são devidamente consultados. Observação: você pode estar pensando, hein, e se na maioria das vezes for um miss
e os dados precisarem ser carregados constantemente do disco para a memória? Sim, teoricamente, é possível, mas a maioria dos compiladores são projetados da maneira que seguelocality of reference
, ou seja, se forem usados dados de algum local da memória, os próximos dados necessários estarão localizados em algum lugar muito próximo, talvez do mesmo page
, page
que acabou de ser carregado na memória. Como resultado, a próxima falha ocorrerá depois de algum tempo, a maioria dos requisitos de memória futuros serão atendidos pela página que acabou de ser trazida ou pelas páginas já na memória que foram usadas recentemente. Exatamente o mesmo princípio nos permite expulsar também o menos usado recentemente page
, com a lógica de que o que não foi usado por um tempo, provavelmente não será usado por um tempo também. Porém, nem sempre é assim e, em casos excepcionais, sim, o desempenho pode ser prejudicado. Mais sobre isso mais tarde.
Solução para o problema 4: Os processos agora podem crescer facilmente; se houver problema de espaço, tudo o que é necessário é fazer uma page
substituição simples , sem mover nenhum outro processo.
Solução para o problema 1: um processo pode acessar memória ilimitada. Quando é necessária mais memória do que a disponível, o disco é usado como backup, os novos dados necessários são carregados na memória a partir do disco e os dados menos usados recentemente frame
(ou page
) são movidos para o disco. Isso pode continuar infinitamente e, como o espaço em disco é barato e virtualmente ilimitado, dá uma ilusão de memória ilimitada. Outra razão para o nome Virtual Memory
, dá a ilusão de memória que não está realmente disponível!
Legal. Anteriormente, estávamos enfrentando um problema em que, embora um processo seja reduzido em tamanho, o espaço vazio é difícil de ser recuperado por outros processos (porque isso exigiria uma compactação cara). Agora é fácil, quando um processo se torna menor em tamanho, muitos dos seus pages
não são mais usados, então quando outros processos precisam de mais memória, um LRU
despejo baseado em simples despeja automaticamente aqueles menos usados pages
da RAM e os substitui pelas novas páginas de os outros processos (e claro atualizar o page tables
de todos esses processos, bem como o processo original que agora requer menos espaço), todos esses sem nenhuma operação de compactação cara!
Solução para o problema 3: sempre que os processos diminuem de tamanho, sua frames
memória RAM será menos usada, portanto, um LRU
despejo de base simples pode remover essas páginas e substituí-las pelas pages
exigidas por novos processos, evitando assim Internal Fragmentation
sem necessidade compaction
.
Quanto ao problema 2, pare um momento para entender isso, o cenário em si foi completamente removido! Não há necessidade de mover um processo para acomodar um novo processo, porque agora o processo inteiro nunca precisa caber de uma vez, apenas algumas páginas dele precisam caber ad hoc, o que acontece com o despejo frames
da RAM. Tudo acontece em unidades de pages
, portanto, não há conceito de hole
agora e, portanto, nenhuma questão de movimento! Dez de maio pages
tiveram que ser movidos devido a esse novo requisito, milhares dos pages
quais foram deixados intactos. Considerando que, anteriormente, todos os processos (cada pedaço deles) tinham que ser movidos!
Solução para o problema 2: para acomodar um novo processo, os dados apenas de partes menos usadas de outros processos devem ser removidos conforme necessário, e isso acontece em unidades de tamanho fixo chamadas pages
. Portanto, não há possibilidade de hole
ou External Fragmentation
com este sistema.
Agora, quando o processo precisa fazer alguma operação de E / S, ele pode abandonar a CPU facilmente! O sistema operacional simplesmente remove todos os seus pages
da RAM (talvez armazene-os em algum cache) enquanto novos processos ocupam a RAM nesse meio tempo. Quando a operação de I / O é feita, o SO simplesmente os restaura pages
para a RAM (claro, substituindo o pages
de alguns outros processos, podem ser daqueles que substituíram o processo original, ou podem ser de alguns que precisam fazer I / Oh, agora, e, portanto, pode renunciar à memória!)
Solução para o problema 5: quando um processo está executando operações de E / S, ele pode facilmente desistir do uso de RAM, que pode ser utilizado por outros processos. Isso leva à utilização adequada da RAM.
E, claro, agora nenhum processo está acessando a RAM diretamente. Cada processo está acessando um local de memória virtual, que é mapeado para um endereço de RAM físico e mantido pelo page-table
desse processo. O mapeamento é apoiado pelo sistema operacional, o sistema operacional permite que o processo saiba qual quadro está vazio para que uma nova página para um processo possa ser ajustada lá. Uma vez que esta alocação de memória é supervisionada pelo próprio sistema operacional, pode facilmente garantir que nenhum processo invada o conteúdo de outro processo, alocando apenas quadros vazios da RAM, ou ao invadir o conteúdo de outro processo na RAM, comunique-se com o processo para atualizá-lo page-table
.
Solução para o problema original: não há possibilidade de um processo acessar o conteúdo de outro processo, uma vez que toda a alocação é gerenciada pelo próprio SO e cada processo é executado em seu próprio espaço de endereço virtual em sandbox.
Então paging
(entre outras técnicas), em conjunto com a memória virtual, é o que alimenta os softwares de hoje rodando em sistemas operacionais! Isso libera o desenvolvedor de software de se preocupar com quanta memória está disponível no dispositivo do usuário, onde armazenar os dados, como evitar que outros processos corrompam os dados de seu software, etc. No entanto, é claro, não é totalmente à prova. Existem falhas:
Paging
é, em última análise, dar ao usuário a ilusão de memória infinita usando o disco como backup secundário. Recuperar dados do armazenamento secundário para caber na memória (chamado page swap
, e o caso de não encontrar a página desejada na RAM é chamado page fault
) é caro, pois é uma operação de E / S. Isso retarda o processo. Várias dessas trocas de página acontecem em sucessão, e o processo se torna dolorosamente lento. Você já viu seu software rodando bem e de repente ele se torna tão lento que quase trava, ou deixa você sem opção de reiniciá-lo? Possivelmente, muitas trocas de página estavam acontecendo, tornando-o lento (chamado thrashing
).
Então, voltando ao OP,
Por que precisamos da memória virtual para executar um processo? - Como a resposta explica longamente, dar aos softwares a ilusão de que o dispositivo / SO tem memória infinita, de forma que qualquer software, grande ou pequeno, possa ser executado, sem se preocupar com a alocação de memória, ou outros processos que corrompam seus dados, mesmo quando correndo em paralelo. É um conceito, implementado na prática por meio de várias técnicas, uma das quais, conforme descrito aqui, é o Paging . Também pode ser segmentação .
Onde fica essa memória virtual quando o processo (programa) do disco rígido externo é trazido para a memória principal (memória física) para a execução? - A memória virtual não está em lugar nenhum por si, é uma abstração, sempre presente, quando o software / processo / programa é inicializado, uma nova tabela de páginas é criada para ele, e contém o mapeamento dos endereços cuspidos por aquele processo para o endereço físico real na RAM. Uma vez que os endereços cuspidos pelo processo não são endereços reais, em certo sentido, eles são, na verdade, o que você pode dizer the virtual memory
,.
Quem cuida da memória virtual e qual é o tamanho da memória virtual? - É cuidado, em conjunto, o SO e o software. Imagine uma função em seu código (que eventualmente foi compilada e transformada no executável que gerou o processo) que contém uma variável local - um int i
. Quando o código é executado, i
obtém um endereço de memória na pilha da função. Essa função é armazenada como um objeto em outro lugar. Esses endereços são gerados pelo compilador (o compilador que compilou seu código no executável) - endereços virtuais. Quando executado, i
deve residir em algum lugar no endereço físico real para a duração dessa função, pelo menos (a menos que seja uma variável estática!), Então o sistema operacional mapeia o endereço virtual gerado pelo compiladori
em um endereço físico real, de modo que sempre que, dentro dessa função, algum código exigir o valor de i
, esse processo pode consultar o sistema operacional para esse endereço virtual e o sistema operacional por sua vez pode consultar o endereço físico para o valor armazenado e retorná-lo.
Suponha que se o tamanho da RAM for 4 GB (ou seja, 2 ^ 32-1 espaços de endereço), qual é o tamanho da memória virtual? - O tamanho da RAM não está relacionado ao tamanho da memória virtual, depende do sistema operacional. Por exemplo, no Windows de 32 bits, sim 16 TB
, no Windows de 64 bits, sim 256 TB
. Obviamente, também é limitado pelo tamanho do disco, já que é nele que é feito o backup da memória.