14 Unidad 6: El Caché de E/S en Sistemas Operativos Modernos
Sesión 2: Gestión Dinámica del Page Cache
En la sesión anterior se han presentado el funcionamiento del caché, ahora nos toca revisar cómo se gestiona en el sistema operativo. Para eso, el sistema operativo utiliza técnicas y algoritmos dependiendo de los requerimientos de cada caso.
Estos mecanismos son el corazón del rendimiento y la fiabilidad de la E/S. Veremos la “inteligencia” del kernel en acción, gestionando activamente este recurso para maximizar la velocidad y garantizar la seguridad de los datos. Para hacer estos procesos complejos más intuitivos, usaremos una analogía central para cada uno.
14.1 Algoritmo de reemplazo: La biblioteca con espacio limitado
Imagina que tu memoria RAM es una mesa de estudio: de acceso muy rápido, pero con espacio limitado. La biblioteca entera es tu disco duro: inmensa, pero lenta.
El dilema es claro: tu mesa está llena de libros (páginas en caché), pero necesitas traer uno nuevo de la estantería (leer del disco). Debes devolver un libro para hacer espacio. ¿Cuál eliges? No querrás devolver el libro que más consultas. El kernel enfrenta este mismo problema y su solución se llama Reclamo de Páginas (Page Reclaim).
En lugar de revisar constantemente qué libro usaste menos (un proceso lento), Linux organiza los libros sobre la mesa en dos pilas:
- Pila Inactiva (
inactive list
): Aquí van los libros nuevos que traes a la mesa. Son candidatos a ser devueltos si no los vuelves a usar pronto. - Pila Activa (
active list
): Si tomas un libro de la pila inactiva y lo consultas de nuevo (es decir, el flagPG_referenced
de la página se activa), el kernel lo considera importante y lo mueve a la pila “activa”.
Cuando se necesita espacio, el kernel siempre toma el libro que ha estado más tiempo en la base de la pila inactiva. De esta forma, protege los libros de uso frecuente y descarta de manera eficiente los que probablemente no se necesiten más.
14.2 Escritura diferida: Dejando los platos sucios para después
Imagina una cocina de restaurante muy concurrida (el Page Cache). Cada vez que un programa modifica datos, es como usar un plato limpio y dejarlo “sucio” (una página marcada como PG_dirty
). Sería una locura lavar cada plato en el instante en que se ensucia; se detendría la producción. De la misma manera, el kernel no escribe cada cambio al disco de inmediato.
El kernel emplea “lavaplatos” (flusher threads
), que son procesos que se ejecutan en segundo plano para escribir las páginas sucias al disco en lotes. Se activan por tres motivos:
- Por Tiempo: Ningún plato puede quedarse sucio más de X segundos. Los
flusher threads
se activan periódicamente para limpiar los datos “viejos” (controlado por el parámetro de kernelvm.dirty_expire_centisecs
). - Por Acumulación: Si los platos sucios ocupan demasiado espacio en la cocina (ej. 10% de la memoria,
vm.dirty_background_ratio
), los lavaplatos empiezan a trabajar para controlar la acumulación. - Por Emergencia: Si la cocina está a punto de colapsar por los platos sucios (ej. 20% de la memoria,
vm.dirty_ratio
), se da la orden de parar casi todo y dedicarse a limpiar. Este es el motivo por el cual el sistema puede experimentar una breve pausa durante operaciones de escritura muy intensas.
14.3 Lectura anticipada: El asistente que se adelanta a tus deseos
Imagina que le pides a tu asistente personal el “Informe Trimestral, Página 1” de un enorme archivador (el disco). Inmediatamente después, le pides la “Página 2”.
El asistente inteligente (el kernel), al notar este patrón de acceso secuencial, no espera. Te entrega la página 2 y, por iniciativa propia, va al archivador y trae las páginas 3, 4 y 5, dejándolas sobre tu escritorio (el Page Cache).
Cuando pides la página 3, la respuesta es instantánea porque ya la tienes a mano. Esta es la lectura anticipada (read-ahead). Es un mecanismo proactivo que mejora drásticamente el rendimiento en la lectura de archivos grandes.
14.4 Control desde el espacio de usuario: El botón de “Guardar ahora”
La estrategia de los “platos sucios” es eficiente, pero arriesgada. Como vimos en la sesión anterior, si hay un apagón repentino, todos los datos en caché que no se habían escrito al disco, se pierden.
Para aplicaciones críticas como una base de datos, la pérdida de datos es inaceptable. Necesitan una forma de ordenarle al sistema que guarde el trabajo de inmediato.
Los programadores tienen “botones de emergencia” para forzar la sincronización del caché con el disco:
fsync()
: Es el botón de “Guardar” por excelencia. Le dice al kernel: “Para este archivo en particular, escribe todos sus datos y metadatos pendientes al disco AHORA y no me respondas hasta que estés seguro de que la operación se completó”. Es la garantía máxima de durabilidad para un archivo.sync()
: Es un botón mucho más drástico: “Guarda TODO lo que esté pendiente en TODO el sistema”. Es menos común y mucho más “pesado” para el rendimiento general del sistema.