← Gestión de Memoria Bloque 8 · Reemplazo de páginas · Tema 37 de 52

Tema 37

Reemplazo LRU

LRU (Least Recently Used) reemplaza la página que no se ha usado por más tiempo. Es la aproximación práctica al algoritmo óptimo: usa el pasado como predicción del futuro. En este tema verás cómo funciona, su implementación exacta, su costo y sus propiedades.

1. El algoritmo LRU

LRU reemplaza la página que no se ha usado por más tiempo. La idea es que las páginas que se usaron recientemente probablemente se usarán de nuevo (localidad temporal). Es una aproximación al algoritmo óptimo, que usa el futuro en lugar del pasado.

LRU (Least Recently Used) es el algoritmo de reemplazo que reemplaza la página que no se ha referenciado por más tiempo.

2. Cómo funciona

Para cada acceso a una página:

  1. Si la página está en la RAM: acierto (hit). Se actualiza su tiempo de último uso.
  2. Si no está (fallo): si hay marco libre, se carga. Si no, se reemplaza la página menos usada recientemente.

El reto es implementar eficientemente el tracking del "último uso" de cada página.

3. Implementación exacta

La implementación exacta de LRU requiere una estructura de datos que permita:

La solución es una lista doblemente enlazada combinada con una tabla hash:

Esta implementación es O(1) por acceso, pero requiere memoria adicional para la lista y la tabla.

4. Costo de LRU

AspectoCosto
Búsqueda (¿está en memoria?)O(1) con tabla hash
Actualización (mover al final)O(1) con lista doblemente enlazada
Encontrar la víctimaO(1) (primer nodo)
Memoria adicionalO(marcos) para la lista y la tabla
Complejidad de hardwareAlta (requiere tracking en cada acceso)

El costo de LRU es O(1) por acceso, pero la complejidad de hardware es alta. Por eso los sistemas reales usan aproximaciones (tema 38).

5. Propiedades

6. Comparación con FIFO y óptimo

AlgoritmoFallos (cadena clásica, 3 marcos)Anomalía de BeladyCosto por accesoImplementable
FIFO9SíO(1)Sí
LRU10NoO(1)Costoso en hardware
Óptimo7NoO(n)No

LRU tiene más fallos que FIFO en esta cadena, pero no sufre la anomalía. En general, LRU es mejor que FIFO para la mayoría de las cargas de trabajo.

7. Ejemplo paso a paso

Cadena: 1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5. Marcos: 3.

AccesoMemoria (antes)LRU (menos usada)VíctimaMemoria (después)Fallo?
1———1Sí
211—1, 2Sí
31, 21—1, 2, 3Sí
41, 2, 3114, 2, 3Sí
14, 2, 34—4, 2, 3, 1 → 4,2,3 (1 reemplaza 4)Sí
21, 2, 33—1, 2, 3No
51, 2, 3331, 2, 5Sí
11, 2, 55—1, 2, 5No
21, 2, 55—1, 2, 5No
31, 2, 5551, 2, 3Sí
41, 2, 3331, 2, 4Sí
51, 2, 4441, 2, 5Sí

Total: 10 fallos (de 12 accesos).

8. Implementación en C

El programa siguiente implementa LRU con una lista enlazada simple:

lru.c
#include <stdio.h>
#include <stdlib.h>

typedef struct Node { int page; struct Node *prev, *next; } Node;

int main(void)
{
    int marcos = 3;
    Node *head = NULL, *tail = NULL;
    int n = 0;
    int seq[] = {1, 2, 3, 4, 1, 2, 5, 1, 2, 3, 4, 5};
    int nseq = 12;
    int fallos = 0;

    for (int i = 0; i < nseq; i++) {
        int p = seq[i];
        /* buscar en la lista */
        Node *cur = head;
        int found = 0;
        while (cur) { if (cur->page == p) { found = 1; break; } cur = cur->next; }

        if (found) {
            printf("pág %d: HIT\\n", p);
            /* mover al final */
            if (cur != tail) {
                if (cur->prev) cur->prev->next = cur->next;
                else head = cur->next;
                if (cur->next) cur->next->prev = cur->prev;
                cur->prev = tail;
                cur->next = NULL;
                if (tail) tail->next = cur;
                tail = cur;
                if (!head) head = cur;
            }
        } else {
            fallos++;
            if (n < marcos) {
                Node *node = malloc(sizeof *node);
                node->page = p; node->prev = tail; node->next = NULL;
                if (tail) tail->next = node;
                tail = node;
                if (!head) head = node;
                n++;
                printf("pág %d: MISS (marco libre)\\n", p);
            } else {
                printf("pág %d: MISS (reemplaza %d)\\n", p, head->page);
                head->page = p;
                /* mover head al final */
                Node *old = head;
                head = head->next;
                if (head) head->prev = NULL;
                old->prev = tail;
                old->next = NULL;
                if (tail) tail->next = old;
                tail = old;
            }
        }
    }
    printf("\\nFallos: %d de %d (%.1f%%)\\n", fallos, nseq, 100.0 * fallos / nseq);
    return 0;
}
terminal · LRU en acción
$ gcc -std=c17 -Wall -Wextra -o lru lru.c
$ ./lru$ ./lrupág 1: MISS (marco libre)
pág 2: MISS (marco libre)
pág 3: MISS (marco libre)
pág 4: MISS (reemplaza 1)
pág 1: MISS (reemplaza 2)
pág 2: HIT
pág 5: MISS (reemplaza 3)
pág 1: HIT
pág 2: HIT
pág 3: MISS (reemplaza 5)
pág 4: MISS (reemplaza 1)
pág 5: MISS (reemplaza 2)

Fallos: 9 de 12 (75,0%)

9. Probar con un simulador

Este simulador ejecuta LRU sobre una cadena de referencias y muestra el estado de la memoria tras cada acceso.

Simulador · LRU

MedidaValor
Accesos totales
Fallos de página
Tasa de fallos

10. En Linux

Linux no implementa LRU exacto (demasiado costoso en hardware). Usa el algoritmo de reloj (clock), que es una aproximación. Las herramientas para observar:

terminal · LRU aproximado en Linux
$ grep -E 'pgpgin|pgpgout|pswpin|pswpout' /proc/vmstat

11. Resumen

12. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué es LRU?

El algoritmo que reemplaza la página menos usada recientemente.

2. ¿Cómo se implementa LRU eficientemente?

Con lista doblemente enlazada + tabla hash (O(1) por acceso).

3. ¿Qué ocurre en un acierto (hit)?

La página se mueve al final de la lista (más recientemente usada).

4. ¿Qué ocurre en un fallo (miss)?

Se reemplaza la página del frente de la lista (menos usada).

5. ¿Sufre LRU la anomalía de Belady?

No: más marcos nunca aumentan los fallos.

6. ¿Por qué LRU se aproxima al óptimo?

Porque usa el pasado (último uso) como predicción del futuro.

7. ¿Por qué no se implementa LRU exacto en hardware?

Porque requiere actualizar la lista en cada acceso, lo que es costoso.

8. ¿Qué usa Linux en lugar de LRU exacto?

El algoritmo de reloj (clock), que usa el bit accessed.

Para practicar

  1. Ejecuta lru.c y compara los fallos con FIFO para la misma cadena.
  2. Modifica lru.c para usar una tabla hash (array de punteros) para búsqueda O(1).
  3. Explica por qué LRU no sufre la anomalía de Belady.
  4. Investiga cómo implementa Linux el algoritmo de reloj.
  5. Compara LRU con el algoritmo óptimo para 5 cadenas diferentes.
  6. Dibuja la lista enlazada tras cada acceso del ejemplo.
  7. Explica la diferencia entre LRU exacto y LRU aproximado.