3 pontos por GN⁺ 2025-11-05 | 1 comentários | Compartilhar no WhatsApp
  • 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 /proc e o papel de ferramentas avançadas de diagnóstico como pagemap e kpageflags
  • 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ão
    • mprotect: altera permissões
    • munmap: 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

  • mmap nã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, offset deve estar alinhado à página, e acessos além do fim do arquivo geram SIGBUS
  • MAP_SHARED reflete diretamente no arquivo, enquanto MAP_PRIVATE cria páginas independentes por copy-on-write (CoW)
  • MAP_FIXED_NOREPLACE garante 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_PRIVATE funcionam pelo mesmo princípio
  • Opções relacionadas
    • vfork: compartilha o espaço de endereços do pai
    • clone(CLONE_VM): cria threads
    • MADV_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, smaps e smaps_rollup, é possível verificar permissões por região, RSS e uso de HugePage
  • /proc/<pid>/pagemap fornece estado por página (presença, swap, PFN etc.), mas o PFN não é exposto a usuários comuns
  • /proc/kpagecount e /proc/kpageflags mostram a contagem de mapeamentos por PFN e propriedades da página (anônima, de arquivo, dirty etc.)
  • mincore e SEEK_DATA/SEEK_HOLE permitem identificar intervalos de dados e buracos em arquivos esparsos
  • É possível implementar dirty tracking em espaço de usuário combinando PAGEMAP_SCAN e userfaultfd

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 khugepaged mescla páginas adjacentes
  • mTHP suporta páginas grandes variáveis (folio) em vários tamanhos, como 16 KiB e 64 KiB
  • É possível verificar o uso em AnonHugePages e FilePmdMapped de /proc/self/smaps
  • As configurações globais do sistema ficam em /sys/kernel/mm/transparent_hugepage/
  • MADV_HUGEPAGE e MADV_NOHUGEPAGE permitem controle por região

Dirty tracking em espaço de usuário

  • Com userfaultfd e PAGEMAP_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
  • PCID e INVPCID fazem a gestão de tags de TLB por processo, reduzindo flushes desnecessários
  • tlb_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 CR3 para usar uma tabela de páginas exclusiva do kernel
    • Usa PCID para minimizar flushes de TLB
  • 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 é
    1. tratar as regras de cache
    2. modificar a tabela de páginas
    3. 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

  • mmapEINVAL: erro de alinhamento do offset do arquivo
  • mmapENOMEM: falta de espaço virtual ou limite de overcommit
  • Acesso a mapeamento de arquivo → SIGBUS: acesso além do EOF
  • mprotect(PROT_EXEC)EACCES: montagem noexec ou política W^X
  • Aumento de RSS após fork(): cópia de páginas por CoW
  • Sobrescrever mapeamento existente com MAP_FIXEDMAP_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_WILLNEED ou fazer acesso prévio
  • Para analisar uso de memória: /proc/<pid>/smaps_rollup/proc/<pid>/maps
  • Em fork de processos grandes: considerar CoW e usar exec no filho
  • Em ambientes sensíveis a latência: observar THP/mTHP, mlock e o comportamento da TLB

1 comentários

 
GN⁺ 2025-11-05
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

    • O tom do texto parece aquele de quando você pede ao Gemini uma explicação fácil
      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

    • Claro, reconheço que esses “atalhos” (cheats) contribuíram para melhorar o desempenho
      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
    • Na verdade, o que o texto explica é o conceito de memória virtual (virtual memory), e isso é uma tecnologia uns 10 anos anterior à 6502
    • É verdade que a complexidade aumentou, mas também ganhamos muita coisa com isso
      Não acho que simplicidade seja necessariamente melhor
    • Mas fico curioso para saber por que você sente tanta saudade dessa simplicidade
  • Aqui aparece que o site foi bloqueado como um domínio perigoso ou inseguro

    • Você está usando, por acaso, um notebook da empresa? O departamento de segurança pode não confiar em domínios .xyz
    • Provavelmente foi um mau funcionamento do software de segurança
      Pelo resultado da verificação no VirusTotal, não há problema algum
    • Parece só um falso positivo (false alarm)
    • Fico curioso sobre qual navegador foi o que bloqueou
    • Engraçado lol
  • Fiquei curioso sobre o que significa dizer que o relatório de erro é apenas “ruído”