← Gestión de Memoria Bloque 7 · Memoria virtual · Tema 30 de 52

Tema 30

Fallos menores y mayores

No todos los fallos de página son iguales. Un fallo menor se resuelve sin leer del disco y cuesta microsegundos; un fallo mayor requiere leer del disco y cuesta milisegundos — mil veces más. En este tema verás la diferencia en detalle, cómo medirlos con getrusage, perf y /proc, cómo interpretar los resultados y cómo reducirlos.

1. El problema: no todos los fallos son iguales

En el tema 29 vimos que un fallo de página ocurre cuando se accede a una página no presente. Pero hay dos tipos: el fallo menor (la página ya está en la RAM) y el fallo mayor (hay que leer del disco). La diferencia en costo es de 1000×. Un programa con 1000 fallos menores tarda ~10 ms; con 1000 fallos mayores, ~10 s.

Saber cuántos fallos de cada tipo tiene un programa es esencial para optimizarlo.

2. Repaso: fallo de página

Un fallo de página ocurre cuando la MMU detecta que una página no está presente (P=0) y lanza una excepción. El SO carga la página en la RAM y reanuda la instrucción (tema 29). La diferencia entre menor y mayor está en dónde está la página antes del fallo.

3. Fallo menor (minor page fault)

Un fallo menor ocurre cuando la página ya está en la RAM pero no está mapeada en el proceso. El SO solo necesita actualizar la tabla de páginas, sin leer del disco.

Casos típicos:

Costo: ~1-10 microsegundos (actualizar la tabla + posiblemente poner a cero la página).

Los fallos menores son normales y esperados: ocurren en la primera escritura a cada página.

4. Fallo mayor (major page fault)

Un fallo mayor ocurre cuando la página no está en la RAM y hay que leerla del disco:

Costo: ~1-10 milisegundos (lectura del disco). Es 1000× más lento que un fallo menor.

Los fallos mayores son señal de un problema: indican que el sistema está leyendo del disco con frecuencia.

5. Comparación detallada

AspectoFallo menorFallo mayor
La página está en la RAMSíNo
Requiere lectura del discoNoSí
Costo típico1-10 µs1-10 ms
Frecuencia esperadaAlta (normal)Baja (debería ser rara)
Causa típicaPrimera escritura, COW, bibliotecas compartidasSwap, mmap de archivo, carga de código
Señal de problemaNoSí (hiperpaginación o carga ineficiente)

6. Medir con getrusage

La función getrusage(RUSAGE_SELF, &ru) devuelve estadísticas del proceso, incluyendo el número de fallos menores (ru_minflt) y mayores (ru_majflt).

leer fallos con getrusage
#include <sys/resource.h>
#include <stdio.h>

struct rusage ru;
getrusage(RUSAGE_SELF, &ru);
printf("Fallos menores: %ld\\n", ru.ru_minflt);
printf("Fallos mayores: %ld\\n", ru.ru_majflt);printf("Fallos mayores: %ld\\n", ru.ru_majflt);

7. Otros métodos de medición

Además de getrusage, hay otras formas de medir los fallos:

HerramientaQué mideCómo se usa
perf statFallos por programa (page-faults, major-faults).perf stat -e page-faults,major-faults ./prog
/proc/vmstatContadores globales del sistema (pgfault, pgmajfault).grep pgfault /proc/vmstat
/proc/PID/statFallos del proceso (minflt, majflt en campos 10 y 12).cat /proc/PID/stat
ValgrindFallos de página y acceso a memoria.valgrind ./prog

8. Experimento en C

El programa siguiente mide los fallos menores y mayores de distintas operaciones:

fallos.c
#define _POSIX_C_SOURCE 200809L
#include <stdio.h>
#include <stdlib.h>
#include <sys/resource.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <unistd.h>

static void medir(const char *nombre)
{
    struct rusage ru;
    getrusage(RUSAGE_SELF, &ru);
    printf("%-22s minflt=%ld majflt=%ld\\n", nombre, ru.ru_minflt, ru.ru_majflt);
}

int main(void)
{
    medir("inicio");

    /* 1. malloc sin tocar: no genera fallos */
    char *p = malloc(1 << 20);
    medir("tras malloc");

    /* 2. Tocar las páginas: fallos menores */
    for (size_t i = 0; i < (1 << 20); i += 4096) p[i] = 1;
    medir("tras tocar");

    /* 3. mmap de archivo: fallos mayores al leer */
    int fd = open("/etc/passwd", O_RDONLY);
    char *m = mmap(NULL, 4096, PROT_READ, MAP_PRIVATE, fd, 0);
    for (int i = 0; i < 4096; i++) (void)m[i];
    medir("tras mmap leer");

    munmap(m, 4096); close(fd);
    free(p);
    medir("final");
    return 0;
}
terminal · medir fallos menores y mayores
$ gcc -std=c17 -Wall -Wextra -o fallos fallos.c
$ ./fallos
inicio              minflt=12 majflt=0
tras malloc         minflt=12 majflt=0
tras tocar          minflt=268 majflt=0
tras mmap leer      minflt=269 majflt=1
final               minflt=269 majflt=1

Los resultados muestran:

9. Interpretar los resultados

Los fallos menores son normales y esperados: ocurren en la primera escritura a una página. Los fallos mayores son los que hay que vigilar: indican que se está leyendo del disco.

Un programa bien diseñado tiene muchos fallos menores y pocos (o cero) fallos mayores. Muchos fallos mayores indican:

La relación minflt/majflt es un indicador de salud: si majflt crece, hay un problema de memoria.

10. Cómo reducir los fallos menores

11. Cómo reducir los fallos mayores

12. Ejemplo numérico: costo total

Un programa tiene 10000 fallos menores y 50 fallos mayores. Costos típicos: fallo menor = 5 µs, fallo mayor = 5 ms.

Los fallos mayores (solo 50) contribuyen 5× más al tiempo total que los 10000 fallos menores. Esto ilustra por qué los fallos mayores son los que hay que vigilar.

13. Probar con un simulador

Este simulador clasifica un escenario como fallo menor o mayor y explica por qué.

Simulador · clasificar fallos

14. En Linux

terminal · fallos en Linux
$ grep -E 'pgfault|pgmajfault' /proc/vmstat
$ perf stat -e page-faults,major-faults ./fallos

15. Resumen

16. Preguntas de repaso

Intenta responder cada una antes de abrir la respuesta.

1. ¿Qué es un fallo menor?

Un fallo en el que la página está en la RAM pero no mapeada en el proceso.

2. ¿Qué es un fallo mayor?

Un fallo que requiere leer la página desde el disco.

3. ¿Cuál es más costoso?

El fallo mayor: miles de veces más lento (ms vs µs).

4. ¿Qué campos de getrusage miden los fallos?

ru_minflt (menores) y ru_majflt (mayores).

5. ¿Qué indica un alto número de fallos mayores?

Que el sistema está usando swap o leyendo archivos del disco con frecuencia.

6. ¿Cómo se reduce un fallo mayor por mmap?

Con madvise (MADV_WILLNEED) para prefetch, o leyendo secuencialmente.

7. ¿Por qué malloc sin tocar no genera fallos?

Porque solo reserva espacio de direcciones; la RAM se entrega al tocar.

8. ¿Qué herramienta cuenta los fallos por programa en Linux?

perf stat -e page-faults,major-faults.

9. ¿Por qué la primera escritura en una página anónima es un fallo menor?

Porque la página se pone a cero en la RAM sin leer del disco.

10. ¿Cómo se interpretan los fallos con madvise?

MADV_WILLNEED provoca fallos mayores anticipados (prefetch); MADV_DONTNEED libera las páginas.

Para practicar

  1. Modifica fallos.c para medir los fallos de un programa que lee un archivo de 10 MiB con mmap.
  2. Compara los fallos de un programa con y sin prefetch (madvise).
  3. Usa perf stat para contar los fallos de ./fallos.
  4. Explica por qué la primera escritura en una página anónima es un fallo menor.
  5. Investiga cómo Linux distingue entre fallo menor y mayor en el manejador.
  6. Dibuja el flujo de un fallo mayor, incluyendo la lectura del disco.
  7. Compara el tiempo de un fallo menor y uno mayor con mediciones reales.
  8. Calcula el costo total para 5000 fallos menores y 20 mayores con costos de 5 µs y 5 ms.
  9. Explica cómo madvise con MADV_SEQUENTIAL afecta a los fallos mayores.
  10. Investiga qué es el read-ahead del kernel y cómo reduce los fallos mayores.