- Explica la estructura de memoria de los procesos en Linux al nivel de funcionamiento real, detallando paso a paso la relación entre el espacio de direcciones virtuales y la memoria física
- Describe de forma concreta cómo un proceso posee y accede a la memoria, centrándose en mecanismos clave como page tables, VMA, mmap, page fault y CoW
- Presenta cómo observar el estado de memoria por proceso mediante el sistema de archivos
/proc, así como el papel de herramientas avanzadas de diagnóstico comopagemapykpageflags - Aborda la optimización de rendimiento y las técnicas de dirty tracking en espacio de usuario mediante funciones recientes del kernel como Transparent Huge Pages (THP), userfaultfd y PAGEMAP_SCAN
- También explica principios de diseño del kernel relacionados con seguridad y rendimiento, como PTI para mitigar Meltdown, flush de TLB y la política W^X, ofreciendo una comprensión integral de la gestión de memoria en Linux
Estructura básica de la memoria de procesos
- Cuando un programa se ejecuta, parece que dispone de una enorme memoria continua, pero en realidad el kernel de Linux la organiza dinámicamente en unidades de páginas
- La CPU consulta las page tables para traducir direcciones virtuales a frames físicos
- Si no existe un mapeo, ocurre un page fault, y el kernel asigna una nueva página o devuelve un error
- Si falta RAM física, el kernel mueve páginas sin uso al disco o elimina páginas de archivo para liberar espacio
/proces un sistema de archivos virtual construido por el kernel en memoria, que expone el estado de los procesos y del kernel en forma de archivos
Espacio de direcciones y VMA
- Cada proceso tiene un objeto de espacio de direcciones, compuesto internamente por varias VMA (Virtual Memory Area)
- Una VMA es un rango contiguo de direcciones con los mismos permisos (R/W/X) y el mismo backend (memoria anónima o archivo)
- Las page tables son estructuras consultadas por el hardware y almacenan la información de mapeo (PTE) entre páginas virtuales y físicas
- Los cambios en el espacio de direcciones se realizan con tres llamadas al sistema
mmap: crea una nueva regiónmprotect: cambia permisosmunmap: elimina un mapeo
- La página es la unidad base de 4 KiB, y algunos sistemas también admiten páginas grandes de 2 MiB y 1 GiB
Ver la composición de memoria con /proc/self/maps
- Con el comando
cat /proc/self/mapsse puede revisar el mapa de memoria del proceso- Aparecen el código, datos y bss del ejecutable, el heap, mapeos anónimos, bibliotecas compartidas, la pila, etc.
- Las regiones
[vdso]y[vvar]son código y datos para llamadas al sistema rápidas mapeados por el kernel
Cómo funciona mmap
mmapno asigna memoria real de inmediato, sino que registra una promesa sobre el espacio de direcciones- Las páginas se asignan en el momento del primer acceso
- Al mapear archivos,
offsetdebe estar alineado a página, y acceder más allá del final del archivo provocaSIGBUS MAP_SHAREDse refleja directamente en el archivo, mientras queMAP_PRIVATEcrea páginas independientes mediante Copy-on-Write (CoW) al escribirMAP_FIXED_NOREPLACEmejora la seguridad al fallar si ya existe un mapeo en la dirección indicada
Primer acceso y page fault
- En el primer acceso a un mapeo nuevo, si la CPU no encuentra la entrada en la page table, ocurre un page fault
- El kernel verifica la validez de la dirección, los permisos de acceso y la existencia del recurso
- Si es un mapeo anónimo, asigna una nueva página rellenada con ceros; si es un mapeo de archivo, la lee desde la page cache
- Un minor fault ocurre cuando los datos ya están en RAM; un major fault, cuando hace falta I/O de disco
- La pila está protegida con una guard page, por lo que un acceso demasiado hacia abajo provoca
SIGSEGV
Copy-on-Write de fork() y MAP_PRIVATE
- Al hacer
fork, padre e hijo comparten las mismas páginas físicas, marcadas como solo lectura- Solo en el momento de escritura se copia una nueva página para mantener la independencia
- Los mapeos de archivo con
MAP_PRIVATEfuncionan con el mismo principio - Opciones relacionadas
vfork: comparte el espacio de direcciones del padreclone(CLONE_VM): crea un hiloMADV_DONTFORK,MADV_WIPEONFORK: excluyen el mapeo del proceso hijo o lo inicializan en cero
Cambio de permisos e invalidación del TLB
- Cuando
mprotectcambia los permisos de una página, el kernel divide la VMA y modifica las page tables, y luego realiza la invalidación del TLB - Según la política W^X, una página no puede ser escribible y ejecutable al mismo tiempo
- El TLB (Translation Lookaside Buffer) es una caché de traducciones recientes de direcciones, y su invalidación provoca una pequeña pausa
Observación detallada mediante /proc
- Con
/proc/<pid>/maps,smapsysmaps_rollupse pueden revisar permisos por región, RSS y uso de HugePage /proc/<pid>/pagemapofrece estado por página (presencia, swap, PFN, etc.), aunque el PFN no está disponible para usuarios normales/proc/kpagecounty/proc/kpageflagsmuestran el número de mapeos por PFN y propiedades de la página (anónima, de archivo, dirty, etc.)- Con
mincoreySEEK_DATA/SEEK_HOLEse pueden identificar regiones de datos y huecos en archivos dispersos - Al combinar
PAGEMAP_SCANyuserfaultfd, es posible implementar dirty tracking en espacio de usuario
Transparent Huge Pages (THP) y mTHP
- THP agrupa automáticamente la memoria de acceso frecuente en páginas grandes (como 2 MiB) para mejorar la eficiencia del TLB
- El hilo
khugepagedfusiona páginas adyacentes
- El hilo
- mTHP admite páginas grandes variables (folio) de distintos tamaños, como 16 KiB y 64 KiB
- En
/proc/self/smaps,AnonHugePagesyFilePmdMappedpermiten verificar su uso - La configuración global del sistema se administra en
/sys/kernel/mm/transparent_hugepage/ - Se puede controlar por región con
MADV_HUGEPAGEyMADV_NOHUGEPAGE
Dirty tracking en espacio de usuario
- Con
userfaultfdyPAGEMAP_SCANse pueden copiar solo las páginas modificadas- El kernel realiza el escaneo y la protección contra escritura en una sola operación atómica
- Es eficiente para snapshots, live migration y casos similares
Mecanismo de flush del TLB
- En x86, la invalidación del TLB se hace de dos maneras
INVLPG: invalida una sola página- Recargar la raíz de las page tables para hacer un flush completo
PCIDeINVPCIDpermiten la gestión de etiquetas de TLB por proceso, reduciendo flushes innecesariostlb_single_page_flush_ceilinges el umbral con el que el kernel decide entre flush por página o flush completo
Mitigación de Meltdown: Page Table Isolation (PTI)
- Meltdown es una vulnerabilidad en la que datos del kernel pueden quedar expuestos a través de la caché durante la ejecución especulativa
- Linux usa PTI (Page Table Isolation) para separar los espacios de direcciones de usuario y kernel
- Al entrar, cambia
CR3para usar page tables exclusivas del kernel - Aprovecha
PCIDpara minimizar el flush del TLB
- Al entrar, cambia
- Está activado por defecto y puede deshabilitarse con
nopti
Procedimiento seguro del kernel para cambiar mapeos
- Al modificar un mapeo, el orden es
- Procesar las reglas de caché
- Modificar las page tables
- Invalidar el TLB
- Los mapeos internos del kernel (
vmap,vmalloc) también sincronizan caché y TLB antes y después de I/O - Algunas arquitecturas requieren flush de la instruction cache después de copiar código
Estructura de pila y llamadas en x86
- En modo de 64 bits se usan los registros RIP, RSP y RBP, y la pila crece hacia abajo
- Según el ABI System V AMD64, los argumentos se pasan en RDI, RSI, RDX, RCX, R8 y R9, y el valor de retorno en RAX
- El modo usuario es ring 3, el kernel es ring 0, y las system calls e interrupciones cambian entre ambos mediante gates
Situaciones de error y diagnóstico
mmap→EINVAL: error de alineación en el offset del archivommap→ENOMEM: falta de espacio virtual o límite de overcommit- Acceso a un mapeo de archivo →
SIGBUS: acceso más allá de EOF mprotect(PROT_EXEC)→EACCES: montajenoexeco política W^X- Aumento de RSS después de
fork(): copia de páginas por CoW - Sobrescribir un mapeo existente con
MAP_FIXED→ se recomiendaMAP_FIXED_NOREPLACE
Checklist práctico
- Para asegurar memoria de inmediato:
mmap+PROT_READ|PROT_WRITE+MAP_PRIVATE|MAP_ANONYMOUS - Al generar código: mantener W^X, usar
mprotect(PROT_READ|PROT_EXEC) - Al mapear archivos: alinear
offseta página, no acceder más allá de EOF - Si hay muchos page faults: usar
MADV_WILLNEEDo acceso anticipado - Para analizar uso de memoria:
/proc/<pid>/smaps_rollup→/proc/<pid>/maps - En
forkde procesos grandes: considerar CoW y usarexecen el hijo - En entornos sensibles a la latencia: observar THP/mTHP,
mlocky el comportamiento del TLB
1 comentarios
Comentarios de Hacker News
Me gustan mucho este tipo de textos breves de explicación
Aunque ya conozca el contenido, me sirve porque puedo volver a confirmarlo mientras lo leo
Cuando veo una frase como “mmap, without the fog”, me da la impresión de que es un texto coescrito por un LLM, y eso me pone incómodo y me irrita sin razón
Además, incluye una expresión rara como “without the fog”, así que da la impresión de que ChatGPT también metió mano
Al leer lo de instruction pipelining, me dan ganas de volver a la época de arquitecturas simples como la 6502
En ese entonces funcionaba “tal cual”, sin mapeos complejos ni proxies
Si hubiera interconexiones rápidas, quizá se podría volver a soñar con esa simplicidad
Pero viendo problemas como Meltdown y Spectre, también está claro que hubo un costo por tanta complejidad
Ahora que la ley de Moore parece estar llegando a su límite, me pregunto si este trade-off de complejidad realmente sigue siendo la mejor opción
No creo que la simplicidad sea necesariamente mejor
Me aparece que el sitio fue bloqueado por ser un dominio peligroso o no seguro
Si ves el resultado de VirusTotal, no parece haber problema
Me pregunto qué significa eso de que el reporte de error es solo “ruido” (noise)