Hoje na aula de sistemas operacionais demos continuidade ao estudo de gerenciamento de memória, desta vez focando em memória virtual. Este é um dos conceitos mais importantes da computação moderna, presente em praticamente todos os sistemas operacionais atuais. Vimos como essa técnica permite que programas executem mesmo quando sua demanda por memória excede a quantidade fisicamente disponível no sistema.
O que é memória virtual?
Memória virtual é uma técnica de gerenciamento de memória que cria uma abstração fundamental: cada processo enxerga um espaço de endereçamento contínuo e privado, como se tivesse a memória inteira para si. O principal objetivo é permitir que múltiplos processos executem simultaneamente sem interferência entre si, além de permitir que programas utilizem mais memória do que a fisicamente disponível na RAM.
A ideia central é separar os endereços lógicos (virtuais) usados pelo programa dos endereços físicos da memória RAM. Essa separação é feita pela MMU (Memory Management Unit), componente de hardware responsável por traduzir endereços virtuais em físicos a cada acesso à memória. Essa abstração dá ao usuário a sensação de ter mais memória disponível do que realmente existe fisicamente, como mencionado na descrição da aula.
Como funciona a tradução de endereços?
Quando um programa acessa um endereço de memória, ele não acessa diretamente a RAM física. Em vez disso, o seguinte fluxo acontece:
- O programa gera um endereço virtual
- A MMU consulta a tabela de páginas do processo
- Se a página estiver na RAM, a MMU traduz o endereço e acessa a posição física correspondente
- Se a página não estiver na RAM, ocorre um page fault e o sistema operacional carrega a página do disco para a memória
Essa tradução acontece a cada acesso à memória, então a eficiência é crítica para o desempenho. Por isso a TLB (Translation Lookaside Buffer) existe: é um cache pequeno e extremamente rápido que guarda as traduções mais recentes, evitando consultas constantes à tabela de páginas na memória principal. Muitas implementações de memória virtual utilizam esse suporte de hardware justamente para obter bom desempenho.
Paginação
O sistema de paginação é a implementação mais comum de memória virtual. A memória virtual é dividida em unidades de tamanho fixo chamadas páginas (geralmente 4 KB), e a memória física é dividida em blocos do mesmo tamanho chamados quadros (frames).
Cada processo tem sua própria tabela de páginas, que mapeia páginas virtuais para quadros físicos. Cada entrada na tabela de páginas contém:
- Bit de presença: indica se a página está carregada na RAM
- Bit de modificação (dirty): indica se a página foi alterada desde que foi carregada
- Bit de acesso: usado por algoritmos de substituição de páginas
- Bits de proteção: permissões de leitura, escrita e execução
Quando um programa acessa uma página que não está na memória física, o hardware gera uma interrupção (page fault) que é tratada pelo sistema operacional. O handler de page fault segue estes passos: determina qual endereço virtual causou o fault, verifica se o endereço é válido (se faz parte do espaço de endereçamento do processo), procura um quadro livre na RAM e, se necessário, executa o algoritmo de substituição para liberar um quadro, carrega a página do disco, atualiza a tabela de páginas e retoma a execução do processo. Esse mecanismo é completamente transparente para o processo.
Em sistemas de 64 bits, uma tabela de páginas simples seria extremamente grande. Para economizar memória, sistemas como o Linux utilizam tabelas de páginas em múltiplos níveis (PGD, P4D, PUD, PMD, PTE), alocando apenas as entradas necessárias e reduzindo significativamente o uso de memória das estruturas de gerenciamento.
A ilusão de mais memória
Um dos pontos mais interessantes da memória virtual é que ela dá ao usuário a sensação de ter mais memória disponível do que realmente existe fisicamente. Isso funciona porque os programas não utilizam toda a sua memória ao mesmo tempo. O sistema operacional mantém na RAM apenas as páginas que estão sendo ativamente usadas — o conjunto de trabalho (working set) do processo — enquanto páginas não acessadas recentemente são movidas para o disco.
Essa ilusão é tão eficaz que muitos usuários não percebem quando o sistema está utilizando swap, a menos que a quantidade de memória seja muito insuficiente e cause lentidão perceptível. O gerenciamento cuidadoso das páginas permite que o sistema execute processos muito maiores que a RAM disponível com degradação de desempenho relativamente pequena na maioria dos cenários.
Algoritmos de substituição de páginas
Quando não há quadros livres na RAM e ocorre um page fault, o sistema operacional precisa decidir qual página remover para liberar espaço. Os principais algoritmos de substituição que vimos na aula são:
- FIFO (First In, First Out): remove a página que está há mais tempo na memória. É simples de implementar, mas pode remover páginas frequentemente usadas e sofre da anomalia de Belady (mais quadros podem piorar a taxa de page faults).
- LRU (Least Recently Used): remove a página menos recentemente usada. Oferece o melhor desempenho teórico, se aproximando do algoritmo ótimo, mas é mais caro de implementar em hardware.
- Clock: uma aproximação eficiente do LRU que utiliza bits de acesso e um ponteiro circular para percorrer as páginas, oferecendo boa relação custo-benefício.
- NRU (Not Recently Used): classifica as páginas em categorias baseadas nos bits de acesso e modificação, removendo páginas de categorias inferiores primeiro.
A escolha do algoritmo impacta diretamente a taxa de page faults e o desempenho geral do sistema. Em sistemas modernos, variações do algoritmo Clock são frequentemente utilizadas por oferecerem bom desempenho com baixo overhead.
SWAP e gerenciamento de memória
O espaço de swap é uma área no disco (partição ou arquivo) que o sistema operacional utiliza para armazenar páginas que não cabem na RAM. Quando um processo precisa de uma página que está no swap, o sistema a carrega de volta para a RAM, possivelmente enviando outra página para o disco em seu lugar.
O problema principal do swap é a lentidão: o disco é ordens de magnitude mais lento que a RAM. Se o sistema passa mais tempo trocando páginas entre RAM e disco do que executando processos, ocorre o fenômeno chamado thrashing, onde a performance degrada drasticamente. Os sintomas típicos são uso baixo de CPU acompanhado de atividade intensa de disco com operações de swap.
Para evitar o thrashing, o sistema operacional tenta manter o working set de cada processo na RAM e utiliza algoritmos de substituição que priorizam páginas que realmente serão utilizadas novamente.
FAQ
P: Qual a diferença entre memória virtual e memória física?
R: A memória física é a RAM real instalada no computador. A memória virtual é uma abstração criada pelo sistema operacional que permite que processos enxerguem um espaço de endereçamento maior e isolado. A MMU faz a tradução entre os dois tipos de endereço de forma transparente.
P: O que acontece se o sistema ficar sem memória RAM e sem espaço de swap?
R: Quando não há mais memória disponível e o swap está cheio, o sistema operacional ativa o OOM Killer (Out-Of-Memory Killer), que seleciona um processo para encerrar e liberar memória. O critério de seleção varia, mas geralmente escolhe o processo que está consumindo mais memória naquele momento.
P: Toda arquitetura de computador suporta memória virtual?
R: A maioria das arquiteturas modernas (x86, ARM, RISC-V) suporta memória virtual com paginação em hardware. Porém, sistemas embarcados muito simples podem não ter suporte, rodando sem MMU. Nesses casos, o sistema operacional gerencia a memória sem proteção entre processos e sem a abstração de endereçamento virtual.