Ir al contenido

Memoria en UNIX, Solaris y Linux

🎯 Al terminar podrás

  1. Relacionar cada idea abstracta (Clock, buddy, multinivel, LRU) con cómo la usa SVR4/Solaris o Linux.
  2. Explicar el reloj de dos manecillas y qué controlan scanrate y handspread.
  3. Aplicar la regla del lazy buddy (Di = Ai − Li) a un caso concreto.
  4. 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 abstractaSVR4 / SolarisLinux
Clock (bit de uso)reloj de dos manecillasantes: Clock con age (tipo LFU) · ahora: split LRU con listas activa e inactiva
Buddylazy buddy para el kernelbuddy para grupos de 1 a 32 frames
Tabla multiniveltabla por proceso + descriptor de bloquePGD → PUD → PMD → PTE
Objetos pequeñoslazy buddyslab (SLAB, SLUB, SLOB)

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)»
EstructuraUna por…Campos importantes
Tabla de páginasproceso (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 discopágina virtualdispositivo 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 swapdispositivo 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():

  1. Padre e hijo comparten los frames, marcados como de solo lectura y con el bit COW.
  2. Cuando uno de los dos escribe, se produce un fallo de protección y el kernel recién entonces copia esa página.
padrepág. 0pág. 1hijopág. 0pág. 1frame 7solo lectura · COWframe 9solo lectura · COWpadrepág. 0pág. 1hijopág. 0pág. 1frame 7solo lectura · COWframe 9queda del padreframe 12copia del hijo · RWjusto después de fork()el hijo escribe en la pág. 1Solo se copió la página escrita. Si el hijo hace exec antes de escribir, no se copia ninguna.

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 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):

  1. La manecilla delantera (fronthand) pone el bit de referencia en 0 en cada página que pasa.
  2. 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.
handspread001011011101traseradelanteragiran en sentido horariodelanterapone R = 0 en cada página que pasa.traserasi R sigue en 0, nadie la usó en el intervalo:va a la lista para expulsar (la de arriba).entre las dosla página marcada con 1 se volvió a usar:cuando pase la trasera, se salva.Menos memoria libre → mayor scanrate → giran más rápido.

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ámetroQué controla
scanratepá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
handspreaddistancia 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.

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 2i2^i y se fusiona enseguida, quizá la próxima petición sea otra vez de 2i2^i y haya que partirlo de nuevo.

El lazy buddy (Barkley y Lee) demora la fusión hasta que parece necesaria. Para cada tamaño 2i2^i lleva estas cuentas:

  • Ni=Ai+Gi+LiN_i = A_i + G_i + L_i:
    • AiA_i: bloques asignados;
    • GiG_i: bloques libres globalmente, que sí se pueden fusionar;
    • LiL_i: bloques libres localmente, que se guardan sin fusionar porque se espera volver a pedirlos.
  • Criterio: que los libres locales no superen a los asignados, Li≤AiL_i \le A_i. Se usa la variable de demora Di=Ai−LiD_i = A_i - L_i:
    • si Di≥2D_i \ge 2, el bloque liberado queda libre local (sin fusionar);
    • si Di=1D_i = 1, se libera globalmente y se fusiona si se puede;
    • si Di=0D_i = 0, se libera globalmente y además se libera globalmente un bloque libre local.
se libera un bloque de 2ⁱD = A − LD ≥ 2queda libre local, sin fusionarD = 1libre global: se fusiona si puedeD = 0libre global + se libera un localA = 5, L = 3 → D = 2A = 4, L = 3 → D = 1(ejemplos del peldaño 2)

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.

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.

9 bitsPGDtabla9 bitsPUDtabla9 bitsPMDtabla9 bitsPTEtabla12 bitsoffsetCR3página4 KB9 + 9 + 9 + 9 + 12 = 48 bits de dirección virtual. Cada tabla tiene 2⁹ = 512 entradas de 8 B = 4 KB.Stallings describe 3 niveles (directory, middle, table); en x86 de 32 bits el middle mide 1 y desaparece.

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.

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.

VersiónAlgoritmo
Antes de 2.6.28Clock 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 LRUdos 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):

  1. Primer acceso a una página inactiva: se pone PG_referenced.
  2. Segundo acceso (en otro barrido): la página pasa a la lista activa. Hacen falta dos accesos en barridos distintos para ser activa.
  3. Si el segundo acceso no llega a tiempo, PG_referenced se borra.
  4. Igual al revés: hacen falta dos «timeouts» para bajar de activa a inactiva.
  5. Las víctimas salen de la lista inactiva, con un criterio tipo LRU.
lista inactivalista activainactivaref = 0accesotimeoutinactivaref = 1accesotimeoutactivaref = 0accesotimeoutactivaref = 1de aquí salen las víctimas (tipo LRU)Subir a activa exige dos accesos en barridos distintos; bajar exige dos timeouts.

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.

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.

buddyentrega 1 páginauna página de 4 KB = 32 huecos de 128 Bkmalloc(100) toma un hueco libre de 128 B (sobran 28 B)ocupadolibre

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.

AsignadorObjetivo
SLABlo 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
1. En el reloj de dos manecillas de SVR4, la manecilla trasera expulsa las páginas con…
2. Si la memoria libre baja, el scanrate…
3. Copy on write sirve para…
4. El lazy buddy…
5. En el split LRU de Linux, una página pasa a la lista activa…
6. slab sirve para…
7. ¿Qué asignador de Linux busca ser lo más compacto posible?

📈 ¿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)