segunda-feira, novembro 06, 2006

Gerenciamento de Memória - Windows 32 bits

Como vimos na parte anterior, a versão 3 do Windows 16 bits finalmente levou os programas ao modo protegido dos processadores x86. Não era, porém, a única opção que a Microsoft fornecia para isto: existia também o OS/2, um gigantesco projeto conjunto com a IBM.

O desenvolvimento do OS/2 foi marcado principalmente pelas diferenças entre a Microsoft e a IBM, em termos de concepção do produto e forma de trabalhar. Esta tensão chegou ao limite após o lançamento da versão 3 do Windows, o que levou a uma separação entre as duas empresas. Esta separação deixou com a Microsoft o que a IBM chamava OS/2 3.0.

Este projeto, iniciado em 1988, era comandado por David Cutler (que tinha larga experiência no projeto de sistemas operacionais adquirida na DEC) sob o nome de NT - New Technoly. No começo de 1991, era anunciado o desenvolvimento do Windows NT e a expansão da interface de programação do Windows de 16 para 32 bits - o Win32. O Windows NT só veio a ser lançado na metade de 93 e teve um aceitação lenta (em parte por exigir a extravagância de 16 Megabytes de Ram, numa das raras ocasiões em que o preço da memória estava subindo ao invés de descer).

Uma das características do NT era a independência do processador utilizado, inicialmente o NT estava disponível não somente para o x86 mas também para processadores RISC como MIPS, Alpha e POWER. Isto influenciou algumas decisões na parte de gerenciamento de memória.

Enquanto que o Windows NT foi escrito praticamente a partir do zero, uma outra implementação do Win32 foi feita a partir do Windows 16 bits, criando o Windows 95.

Para tentar ajudar a migração aos 32 bits, existia uma "adaptação técnica" que implementava um subconjunto do Win32 sobre o Windows 16 bits - o chamado Win32s. Para os fins deste post vamos ignorar o Win32s.

O Mapa da Memória

No Win32 os recursos de gerenciamento de memória do processador são usados para dar a cada processo o seu próprio espaço de endereçamento com 4GB (é claro que a maior parte desde espaço normalmente não tem memória física associada).



No Windows 9x (95, 98 e Me), os 4GB são divididos da seguinte forma:
  • os primeiros 4M (endereços 0x00000000 a 0x003FFFFF) são reservados para o sistema operacional (DOS e Windows 16 bits) e não devem ser acessados pelas aplicações (porém apenas os primeiros 4K estão protegidos)
  • os bytes seguintes até a metade do espaço de endereçamento (0x00400000 a 0x7FFFFFFF) estão diponíveis para o processo. Esta memória é privada ao processo; outros processos não podem acesssá-la.
  • em seguida existe 1G (de 0x80000000 a 0xBFFFFFFF) de endereçamento compartilhado por todos os processos. É nesta região que são colocados os arquivos mapeados em memória e DLLs compartilhadas.
  • O último gigabyte (0xC0000000 a 0xFFFFFFFF) contém o sistema operacional e não deve ser acessado pelas aplicações (porém não está protegido e é compartilhado por todos os processos)
Portanto, embora o Windows 9x protega as aplicações entre elas, ele não protege o sistema operacional em si.

No Windows NT (e 2000 e XP), os 4GB são divididos um pouco diferente:
  • os primeiros 64K (0x00000000 a 0x0000FFFF) são inacessíveis, para facilitar a detecção de ponteiros inválidos (tipicamente com NULL).
  • de 0x00010000 a 0x7FFEFFFF (2G - 128K) fica a região privada do processo. Ela é usada para o código e dados do processo, os arquivos mapeados em memória e as DLLs.
  • os 64K no final da primeira metade (0x7FFF0000 a 0x7FFFFFFF) são inacessíveis, para facilitar a detecção de ponteiros inválidos.
  • os dois últimos gigabytes (0x80000000 a 0xFFFFFFFF) contém o sistema operacional e não podem ser acessados pela aplicação.
Portanto o Windows NT é extremamente rigoros em termos de separação entre os processos e de proteção da memória ocupada pelo sistema operacional.

Usando o Espaço de Endereçamento

Quando um processo é criado, a maior parte do seu espaço de endereçamento é marcada como livre. Uma tentativa de acessar um destes endereços causará um erro.

O primeiro passo apra usar a memória é alocar uma região usando a função VirtualAlloc. A alocação não associa memória física à região, apenas a marca como em uso. O endereço inicial da região deve ser múltiplo da granularidade de alocação (64KB) e o tamanho deve ser múltiplo do tamanho da página do mecanismo de memória virtual (o que varia conforme o processador). A função VirtualFree libera uma região.

O passo seguinte é associar memória às páginas da região (commit). O Commit é controlado no nível de página, portanto não é necessário associar todas as páginas da região de uma só vez. O commit é feito também pela VirtualAlloc; VirtualFree pode ser usada para cancelar o commit liberando a memória. A memória associda não corresponde necessáriamente a RAM física, o Win32 utiliza os recursos de memória virtual do processador para controlar a movimentação das páginas entre a Ram e o disco (arquivo de paginação), simulando uma memória Ram maior que a real se necessário.

Arquivos Mapeados na Memória

O mesmo recurso que é usado para simular uma memória Ram maior que a real, através da movimentação de páginas entre a Ram e o arquivo de paginação pode ser usado com arquivos comuns.

O resultado são arquivos mapeados em memória: do ponto de vista do programador é como se todo o arquivo tivesse sido carregado para a memória, ficando acessível via ponteiros ao invés de funções de acesso a arquivo. Isto é usado no Win32 de três formas principais:

  • para carregar e executar arquivos EXE e DLL. Desta forma a paginação do código é feita diretamente entre a Ram e o arquivo original, não envolvendo o arquivo de paginação.
  • para simplificar o acesso a dados em um arquivo. O programa manipula o arquivo através de ponteiros sem precisar se preocupar em chamar as funções de acesso a arquivo ou gerenciar buffers de leitura e escrita.
  • como forma de compartilhamento de dados e inter-comunicação entre processos rodando em uma máquina. Dois ou mais processos podem mapear em memória o mesmo arquivo, desta forma as alterações feitas por um são visíveis a todos.

Um comentário:

Fábio disse...

Muito bom seu site. Você poderia abordar em um artigo as diferenças e usos das várias funções de alocação de memória(new, malloc, HeapAlloc, VirtualAlloc, etc.).