- Explica a estrutura de memória de processos no Linux no nível do funcionamento real, detalhando passo a passo a relação entre espaço de endereços virtual e memória física
- Explica concretamente como os processos possuem e acessam memória com foco em mecanismos centrais como tabelas de páginas, VMA, mmap, page fault e CoW
- Apresenta como observar o estado da memória por processo por meio do sistema de arquivos
/proce o papel de ferramentas avançadas de diagnóstico comopagemapekpageflags - Aborda otimização de desempenho e técnicas de dirty tracking em espaço de usuário com recursos recentes do kernel como Transparent Huge Pages (THP), userfaultfd e PAGEMAP_SCAN
- Também explica princípios de projeto do kernel ligados a segurança e desempenho, como PTI contra Meltdown, flush de TLB e a política W^X, oferecendo uma visão geral do gerenciamento de memória no Linux
Estrutura básica da memória de processos
- Quando um programa é executado, parece que existe uma memória contínua enorme, mas na prática o kernel Linux a organiza dinamicamente em unidades de página
- A CPU consulta a tabela de páginas para converter endereços virtuais em frames físicos
- Se não houver mapeamento, ocorre um page fault, e o kernel aloca uma nova página ou retorna um erro
- Se faltar RAM física, o kernel move páginas não utilizadas para o disco ou remove páginas de arquivo para liberar espaço
/procé um sistema de arquivos virtual montado pelo kernel na memória, que expõe o estado de processos e do kernel como arquivos
Espaço de endereços e VMA
- Cada processo possui um objeto de espaço de endereços, composto internamente por várias VMAs (Virtual Memory Areas)
- Uma VMA é um intervalo contínuo de endereços com as mesmas permissões (R/W/X) e o mesmo backend (memória anônima ou arquivo)
- A tabela de páginas é a estrutura consultada pelo hardware e armazena as informações de mapeamento (PTE) entre páginas virtuais e físicas
- Mudanças no espaço de endereços são feitas com três chamadas de sistema
mmap: cria uma nova regiãomprotect: altera permissõesmunmap: remove o mapeamento
- A página tem 4 KiB como unidade básica, e alguns sistemas também suportam páginas grandes de 2 MiB e 1 GiB
Vendo a composição da memória com /proc/self/maps
- É possível verificar o mapa de memória do processo com o comando
cat /proc/self/maps- São exibidos código, dados e bss do executável, heap, mapeamentos anônimos, bibliotecas compartilhadas, stack etc.
- As regiões
[vdso]e[vvar]são código e dados para chamadas de sistema rápidas mapeados pelo kernel
Como mmap funciona
mmapnão faz a alocação real de memória; ele registra uma promessa sobre o espaço de endereços- As páginas só são alocadas no primeiro acesso
- Em mapeamentos de arquivo,
offsetdeve estar alinhado à página, e acessos além do fim do arquivo geramSIGBUS MAP_SHAREDreflete diretamente no arquivo, enquantoMAP_PRIVATEcria páginas independentes por copy-on-write (CoW)MAP_FIXED_NOREPLACEgarante segurança ao falhar se já existir um mapeamento no endereço especificado
Primeiro acesso e page fault
- No primeiro acesso a um novo mapeamento, se a CPU não encontrar a entrada na tabela de páginas, ocorre um page fault
- O kernel verifica validade do endereço, permissões de acesso e existência
- Em mapeamento anônimo, aloca uma nova página zerada; em mapeamento de arquivo, lê a partir do page cache
- minor fault ocorre quando os dados já estão na RAM, enquanto major fault exige I/O de disco
- A stack é protegida por uma guard page, então acessos muito abaixo provocam
SIGSEGV
fork() e o Copy-on-Write de MAP_PRIVATE
- Ao executar
fork, pai e filho compartilham as mesmas páginas físicas, marcadas como somente leitura- Só no momento da escrita uma nova página é copiada para manter a independência
- Mapeamentos de arquivo com
MAP_PRIVATEfuncionam pelo mesmo princípio - Opções relacionadas
vfork: compartilha o espaço de endereços do paiclone(CLONE_VM): cria threadsMADV_DONTFORK,MADV_WIPEONFORK: excluem o mapeamento do processo filho ou o reinicializam com zero
Mudança de permissões e invalidação de TLB
- Ao alterar permissões de páginas com
mprotect, o kernel faz divisão de VMA e modificação da tabela de páginas e depois executa a invalidação de TLB - Pela política W^X, uma página não pode ser gravável e executável ao mesmo tempo
- A TLB (Translation Lookaside Buffer) é um cache de traduções recentes de endereços, e sua invalidação causa uma pequena latência
Observação detalhada com /proc
- Com
/proc/<pid>/maps,smapsesmaps_rollup, é possível verificar permissões por região, RSS e uso de HugePage /proc/<pid>/pagemapfornece estado por página (presença, swap, PFN etc.), mas o PFN não é exposto a usuários comuns/proc/kpagecounte/proc/kpageflagsmostram a contagem de mapeamentos por PFN e propriedades da página (anônima, de arquivo, dirty etc.)mincoreeSEEK_DATA/SEEK_HOLEpermitem identificar intervalos de dados e buracos em arquivos esparsos- É possível implementar dirty tracking em espaço de usuário combinando
PAGEMAP_SCANeuserfaultfd
Transparent Huge Pages (THP) e mTHP
- THP agrupa automaticamente memória acessada com frequência em páginas grandes (como 2 MiB) para melhorar a eficiência da TLB
- A thread
khugepagedmescla páginas adjacentes
- A thread
- mTHP suporta páginas grandes variáveis (folio) em vários tamanhos, como 16 KiB e 64 KiB
- É possível verificar o uso em
AnonHugePageseFilePmdMappedde/proc/self/smaps - As configurações globais do sistema ficam em
/sys/kernel/mm/transparent_hugepage/ MADV_HUGEPAGEeMADV_NOHUGEPAGEpermitem controle por região
Dirty tracking em espaço de usuário
- Com
userfaultfdePAGEMAP_SCAN, é possível copiar apenas as páginas alteradas- O kernel executa a varredura e a proteção contra escrita em uma única operação atômica
- Isso é eficiente em snapshots, live migration e cenários semelhantes
Mecanismo de flush de TLB
- No x86, a invalidação da TLB acontece de duas formas
INVLPG: invalida uma única página- recarregar a raiz da tabela de páginas para fazer flush completo
PCIDeINVPCIDfazem a gestão de tags de TLB por processo, reduzindo flushes desnecessáriostlb_single_page_flush_ceilingé o limite usado pelo kernel para escolher entre flush por página e flush completo
Mitigação de Meltdown: Page Table Isolation (PTI)
- Meltdown é uma vulnerabilidade na qual dados do kernel podem ser expostos pelo cache durante execução especulativa
- O Linux usa PTI (Page Table Isolation) para separar os espaços de endereços de usuário e kernel
- Na entrada, troca
CR3para usar uma tabela de páginas exclusiva do kernel - Usa
PCIDpara minimizar flushes de TLB
- Na entrada, troca
- Isso vem ativado por padrão, e pode ser desabilitado com
nopti
Procedimento seguro do kernel para mudar mapeamentos
- Ao alterar um mapeamento, a ordem é
- tratar as regras de cache
- modificar a tabela de páginas
- invalidar a TLB
- Mapeamentos internos do kernel (
vmap,vmalloc) também sincronizam cache e TLB antes e depois de I/O - Em algumas arquiteturas, é necessário fazer flush do cache de instruções após copiar código
Estrutura de stack e chamadas no x86
- No modo 64 bits, são usados os registradores RIP, RSP e RBP, e a stack cresce para baixo
- Conforme a ABI System V AMD64, os argumentos são passados em RDI, RSI, RDX, RCX, R8 e R9, e o valor de retorno fica em RAX
- O modo de usuário roda em ring 3, o kernel em ring 0, e system calls e interrupções fazem a transição por meio de gates
Situações de erro e diagnóstico
mmap→EINVAL: erro de alinhamento do offset do arquivommap→ENOMEM: falta de espaço virtual ou limite de overcommit- Acesso a mapeamento de arquivo →
SIGBUS: acesso além do EOF mprotect(PROT_EXEC)→EACCES: montagemnoexecou política W^X- Aumento de RSS após
fork(): cópia de páginas por CoW - Sobrescrever mapeamento existente com
MAP_FIXED→MAP_FIXED_NOREPLACEé recomendado
Checklist prático
- Para garantir memória imediatamente:
mmap+PROT_READ|PROT_WRITE+MAP_PRIVATE|MAP_ANONYMOUS - Ao gerar código: manter W^X e usar
mprotect(PROT_READ|PROT_EXEC) - Ao mapear arquivos: alinhar
offsetà página e evitar acesso além do EOF - Se houver muitos page faults: usar
MADV_WILLNEEDou fazer acesso prévio - Para analisar uso de memória:
/proc/<pid>/smaps_rollup→/proc/<pid>/maps - Em
forkde processos grandes: considerar CoW e usarexecno filho - Em ambientes sensíveis a latência: observar THP/mTHP,
mlocke o comportamento da TLB
1 comentários
Comentários do Hacker News
Gosto muito desses textos explicativos curtos
Mesmo quando já conheço o conteúdo, ajuda poder revisá-lo mais uma vez enquanto leio
Quando vejo uma expressão como “mmap, without the fog”, fico com a sensação de que foi um texto coescrito por LLM, e isso me deixa incomodado e irritado sem motivo
E ainda tem uma expressão estranha como “without the fog”, então passa a impressão de que o ChatGPT ajudou a escrever
Ao ver a conversa sobre instruction pipelining, bate uma vontade de voltar à época de arquiteturas simples como a 6502
Naquele tempo, tudo funcionava “como era”, sem mapeamentos complexos nem proxies
Se houvesse interconexões rápidas o bastante, talvez desse até para sonhar de novo com esse tipo de simplicidade
Mas problemas como Meltdown e Spectre mostram claramente que houve um preço a pagar pelo aumento da complexidade
Agora que a lei de Moore parece estar chegando ao limite, fica a dúvida se esse trade-off de complexidade ainda é mesmo a melhor escolha
Não acho que simplicidade seja necessariamente melhor
Aqui aparece que o site foi bloqueado como um domínio perigoso ou inseguro
Pelo resultado da verificação no VirusTotal, não há problema algum
Fiquei curioso sobre o que significa dizer que o relatório de erro é apenas “ruído”