Memoria en UNIX, Solaris y Linux
🎯 Al terminar podrás
- Relacionar cada idea abstracta (Clock, buddy, multinivel, LRU) con cómo la usa SVR4/Solaris o Linux.
- Explicar el reloj de dos manecillas y qué controlan scanrate y handspread.
- Aplicar la regla del lazy buddy (Di = Ai − Li) a un caso concreto.
- Describir cómo una página sube a la lista activa en el split LRU, y para qué sirve slab.
Antes necesitas: Clock · Buddy system · Tablas multinivel
- Visual mira el diagrama o la tabla y explica qué cambia en cada paso
- Socrático responde la pregunta guía antes de seguir leyendo
- Feynman explícalo en voz alta sin mirar y compara con la lista
Mapa de la página: cada mecanismo real es una idea que ya conoces, ajustada para memorias grandes.
| Idea abstracta | SVR4 / Solaris | Linux |
|---|---|---|
| Clock (bit de uso) | reloj de dos manecillas | antes: Clock con age (tipo LFU) · ahora: split LRU con listas activa e inactiva |
| Buddy | lazy buddy para el kernel | buddy para grupos de 1 a 32 frames |
| Tabla multinivel | tabla por proceso + descriptor de bloque | PGD → PUD → PMD → PTE |
| Objetos pequeños | lazy buddy | slab (SLAB, SLUB, SLOB) |
UNIX SVR4 y Solaris (§8.3)
Sección titulada «UNIX SVR4 y Solaris (§8.3)»SVR4 tiene dos esquemas separados:
- el sistema de paginación, para la memoria virtual de los procesos y para la caché de bloques de disco;
- el asignador de memoria del kernel, para los objetos del kernel.
Estructuras del sistema de paginación (tabla 8.6)
Sección titulada «Estructuras del sistema de paginación (tabla 8.6)»| Estructura | Una por… | Campos importantes |
|---|---|---|
| Tabla de páginas | proceso (una entrada por página virtual) | nº de frame · age (cuánto tiempo sin referenciar; depende del procesador) · copy on write · modify · reference · valid · protect |
| Descriptor de bloque de disco | página virtual | dispositivo de swap · nº de bloque · tipo de almacenamiento (área de swap o el ejecutable; si hay que poner el frame en ceros antes de cargarlo) |
| Tabla de datos de frames (pfdata) | frame físico (indexada por nº de frame) | estado de la página (en swap, en el ejecutable o con DMA en curso) · contador de referencias (procesos que la usan) · dispositivo y bloque de la copia · punteros a la lista libre y a la cola hash |
| Tabla de uso de swap | dispositivo de swap (una entrada por página del dispositivo) | contador de PTE que apuntan a esa página en disco · identificador |
Copy on write (COW) es lo que hace barato a fork():
- Padre e hijo comparten los frames, marcados como de solo lectura y con el bit COW.
- Cuando uno de los dos escribe, se produce un fallo de protección y el kernel recién entonces copia esa página.
Cómo leerlo: a la izquierda, después del fork: las dos tablas apuntan a los mismos frames, marcados de solo lectura. A la derecha, el hijo escribió en su página 1: el fallo de protección hace que el kernel copie solo esa página a un frame nuevo (verde) y cambie la flecha del hijo. La página 0 sigue compartida.
Si el hijo hace exec enseguida, casi no se copia nada (ver fork).
El reloj de dos manecillas (fig. 8.21)
Sección titulada «El reloj de dos manecillas (fig. 8.21)»El reemplazo usa la tabla de frames. Los frames libres están en una lista; cuando bajan de un umbral, el kernel «roba» frames. El algoritmo es un Clock refinado con dos manecillas que recorren la lista de páginas elegibles (no bloqueadas):
- La manecilla delantera (fronthand) pone el bit de referencia en 0 en cada página que pasa.
- Un rato después pasa la manecilla trasera (backhand) por la misma página:
- si el bit está en 1, la página se usó en ese intervalo: se deja;
- si sigue en 0, nadie la tocó: va a la lista para expulsar.
Cómo leerlo: cada círculo es una página y el número es su bit R. La delantera va adelante apagando bits; la trasera la sigue a una distancia fija (el handspread, arco punteado). Las páginas entre las dos tienen un tiempo para volver a usarse: la que tiene un 1 se usó en ese intervalo y se salvará. La de arriba sigue en 0 cuando llega la trasera, así que se expulsa. Ejemplo propio.
| Parámetro | Qué controla |
|---|---|
| scanrate | páginas por segundo que recorren las manecillas. Varía linealmente entre slowscan y fastscan a medida que la memoria libre baja de lotsfree a minfree: con menos memoria libre, las manecillas giran más rápido |
| handspread | distancia entre las dos manecillas |
Juntos determinan la ventana de oportunidad: cuánto tiempo tiene una página para ser usada antes de que la expulsen.
¿Por qué dos manecillas y no una (el Clock normal)?
Con un puntero, el tiempo entre apagar el bit y volver a revisarlo es una vuelta completa. Con memorias enormes, eso puede tardar muchísimo, y casi todas las páginas tendrían el bit en 1 (todo parece «usado»). Con dos manecillas, el intervalo de observación queda fijado por el handspread y el scanrate, y no por el tamaño de la memoria. Además se puede ajustar en caliente según la presión de memoria.
Asignador del kernel: lazy buddy
Sección titulada «Asignador del kernel: lazy buddy»El kernel crea y destruye objetos pequeños todo el tiempo: buffers de rutas, buffers de STREAMS, estructuras proc, vnodes, zombies. Paginar para eso sería ineficiente.
SVR4 usa un buddy (ver buddy system), pero el buddy pierde tiempo en partir y fusionar: si liberas un bloque de y se fusiona enseguida, quizá la próxima petición sea otra vez de y haya que partirlo de nuevo.
El lazy buddy (Barkley y Lee) demora la fusión hasta que parece necesaria. Para cada tamaño lleva estas cuentas:
- :
- : bloques asignados;
- : bloques libres globalmente, que sí se pueden fusionar;
- : bloques libres localmente, que se guardan sin fusionar porque se espera volver a pedirlos.
- Criterio: que los libres locales no superen a los asignados, . Se usa la variable de demora :
- si , el bloque liberado queda libre local (sin fusionar);
- si , se libera globalmente y se fusiona si se puede;
- si , se libera globalmente y además se libera globalmente un bloque libre local.
Cómo leerlo: D mide cuánta demanda hay: muchos bloques asignados frente a pocos guardados. Con D alto conviene guardar el bloque sin fusionar, porque lo van a volver a pedir. Con D bajo ya sobran guardados, así que se fusiona.
Peldaño 2 · Complétalofaltan pasos clave: llénalos tú
Complétalo: lazy buddy para bloques de 2i
Para cierto tamaño hay Ai = 5 bloques asignados y Li = 3 libres locales. Se libera un bloque. Calcula Di = Ai − Li y decide qué pasa con él. Después repite con Ai = 4 y Li = 3.
Comparar
- A = 5, L = 3 → D = 2 ≥ 2: el bloque queda libre local, sin fusionar.
- A = 4, L = 3 → D = 1: se libera globalmente y se fusiona con su buddy si este está libre.
- La idea: mientras haya muchos asignados en comparación con los guardados, conviene guardar el bloque para la próxima petición del mismo tamaño.
Linux (§8.4)
Sección titulada «Linux (§8.4)»La unidad básica es la página física (struct page), normalmente de 4 KB. Linux también soporta HugePages (por ejemplo, 2 MB), que reducen los fallos de TLB. DPDK las usa para los buffers de red.
Direccionamiento virtual: tablas de varios niveles
Sección titulada «Direccionamiento virtual: tablas de varios niveles»En el modelo que describe Stallings, la dirección virtual tiene cuatro campos:
┌──────────────────┬──────────────────────┬────────────────┬────────┐│ page directory │ page middle directory│ page table │ offset │└──────────────────┴──────────────────────┴────────────────┴────────┘- Page directory: uno por proceso activo, del tamaño de una página y siempre en RAM.
- Page middle directory: puede ocupar varias páginas.
- Page table: cada entrada apunta a una página virtual.
Se diseñó para el Alpha de 64 bits (tres niveles en hardware). En x86 de 32 bits, que tiene dos niveles, el middle directory se define de tamaño 1 y el compilador elimina ese nivel: no hay costo en tiempo de ejecución. El x86-64 actual usa 4 niveles (PGD → PUD → PMD → PTE), y 5 con LA57. La idea es la misma: tabla multinivel.
Cómo leerlo: es el diagrama de dos niveles de paginación, pero con cuatro tablas encadenadas. Cada campo de 9 bits elige una de las 512 entradas de su tabla, y esa entrada dice dónde está la tabla siguiente. CR3 apunta a la primera y el offset baja directo a la página.
Asignación de páginas: buddy
Sección titulada «Asignación de páginas: buddy»Para leer y escribir bloques contiguos eficientemente, Linux mantiene grupos de 1, 2, 4, 8, 16 o 32 frames contiguos y los parte o fusiona con el algoritmo buddy.
Reemplazo: de «age» al split LRU
Sección titulada «Reemplazo: de «age» al split LRU»| Versión | Algoritmo |
|---|---|
| Antes de 2.6.28 | Clock con una variable age de 8 bits en lugar del bit de uso. Cada acceso la incrementa y un barrido periódico la decrementa. Una página con age = 0 es la mejor víctima. Es una forma de LFU (menos frecuentemente usada). Problema: los barridos cuestan cada vez más CPU con memorias grandes |
| Desde 2.6.28: split LRU | dos listas por zona de memoria, activa e inactiva, y dos flags por página, PG_active y PG_referenced. El demonio kswapd recorre periódicamente las PTE y limpia los bits de acceso |
Ciclo de vida de una página (fig. 8.24):
- Primer acceso a una página inactiva: se pone PG_referenced.
- Segundo acceso (en otro barrido): la página pasa a la lista activa. Hacen falta dos accesos en barridos distintos para ser activa.
- Si el segundo acceso no llega a tiempo, PG_referenced se borra.
- Igual al revés: hacen falta dos «timeouts» para bajar de activa a inactiva.
- Las víctimas salen de la lista inactiva, con un criterio tipo LRU.
Cómo leerlo: cada acceso mueve la página una casilla a la derecha (verde) y cada timeout del barrido, una a la izquierda. Desde la izquierda del todo hasta la lista activa hacen falta dos accesos; para volver, dos timeouts. Así una página usada una sola vez no desplaza a las que se usan seguido.
Memoria del kernel: slab
Sección titulada «Memoria del kernel: slab»La base es el asignador de páginas (buddy), pero su unidad mínima es una página. Para trozos pequeños y de tamaños raros, Linux usa slab dentro de páginas ya asignadas. En x86 los trozos son de 32, 64, 128, 252, 508, 2040 o 4080 bytes, con una lista por tamaño.
Cómo leerlo: el buddy solo sabe dar páginas enteras. Slab toma una y la corta en huecos iguales para un tamaño de objeto: pedir memoria es tomar un hueco libre y liberar es devolverlo. Lo que sobra dentro del hueco (28 B en el ejemplo) es fragmentación interna. El dibujo usa como ejemplo una caché de 128 B.
| Asignador | Objetivo |
|---|---|
| SLAB | lo más amigable posible con la caché (menos fallos de caché); el más usado |
| SLUB (unqueued slab) | simple, con menos instrucciones (el predeterminado en los kernels actuales) |
| SLOB (simple list of blocks) | lo más compacto posible, para sistemas con poca memoria |
Autoevaluación
0/7 correctas · 0 respondidas📈 ¿Qué tan bien lo dominas?
Elige pensando en lo que hiciste, no en lo que leíste. Para marcar 3 tienes que haber resuelto un ejercicio nuevo sin pistas y haberlo justificado. Si solo lo leíste y sientes que «ya entendí», todavía es un 1.
Fuente ·Stallings 9e §8.3 (UNIX and Solaris Memory Management: tabla 8.6, fig. 8.21, lazy buddy fig. 8.22) · Stallings 9e §8.4 (Linux Memory Management: fig. 8.23, 8.24, slab)