La lección anterior terminó con un dato que merece explicación. filefrag nos dijo que los 4.219 bloques de 2026-08-31.dat están en un solo extent, perfectamente contiguos del bloque 8.394.271 al 8.398.489. Y sin embargo ese fichero se escribió 24 bytes cada 125 milisegundos durante 24 horas, mientras meteo-api, el agregador, los logs del sistema y media docena de servicios más escribían en el mismo volumen. ¿Cómo acaba contiguo un fichero escrito así?
Esa es la primera mitad de esta lección: cómo decide un sistema de archivos qué bloques ocupa un fichero, cómo lleva la cuenta de los que están libres, y cómo representa esa lista dentro de un inodo que solo tiene 60 bytes para punteros. Recorreremos las cuatro estrategias históricas —contigua, enlazada, indexada y por extents— con los números concretos de Meteora, y entenderás de paso por qué FAT32 es lenta con ficheros grandes y por qué desfragmentar ya no tiene sentido.
La segunda mitad es más grave. Añadir un bloque a un fichero no es una operación, son tres: escribir el dato, marcar el bloque como ocupado en el mapa de bits, y actualizar el inodo con su nueva dirección y tamaño. Si se corta la luz entre esas tres escrituras, el sistema de archivos queda en un estado incoherente que puede ir desde una pérdida de espacio inocua hasta la corrupción cruzada de dos ficheros. Veremos cómo el diario (journal) convierte tres operaciones en una transacción atómica, qué garantiza exactamente cada uno de los tres modos de ext4, y por qué la consistencia de metadatos no es lo mismo que la integridad de los datos: un bit que se corrompe en el plato pasa desapercibido para ext4, para XFS y para el RAID 1 de Meteora.
Contenido
- Asignación contigua: rápida y frágil
- Asignación enlazada y la tabla FAT
- Asignación indexada: el inodo y sus punteros indirectos
- Extents: la solución moderna, medida sobre Meteora
- Gestión del espacio libre
- Fragmentación: por qué ext4 y XFS fragmentan poco
- El problema de la consistencia: tres escrituras y un corte de luz
fscky por qué su coste es inaceptable- El diario: transacción, confirmación, aplicación y recuperación
- Los tres modos de ext4 y cuál elige Meteora
- Alternativas al diario: copia al escribir y log-structured
- Integridad de datos: sumas de verificación y corrupción silenciosa
- Barreras de escritura y discos que mienten
- Instantáneas y copias de seguridad: otra capa
Asignación contigua: rápida y frágil
La idea más simple: cada fichero ocupa un conjunto de bloques consecutivos. El inodo solo necesita guardar dos números, el bloque inicial y la longitud.
Sus ventajas son notables. La lectura secuencial es óptima: una sola petición de E/S trae el fichero entero, sin buscar nada. El acceso aleatorio es trivial: el bloque del byte N está en inicio + N/4096, una división. Y los metadatos son mínimos: ocho bytes describen un fichero de cualquier tamaño.
Pero tiene dos problemas que la descartan para uso general:
Fragmentación externa. Al crear y borrar ficheros de tamaños distintos, el espacio libre queda troceado en huecos pequeños. Puedes tener 40 GB libres y no poder crear un fichero de 100 MB porque el hueco contiguo más grande son 60 MB. Es exactamente la fragmentación externa que vimos en Gestión de Memoria con la asignación por particiones variables, y la solución sería igual de cara: compactar, es decir, mover físicamente gigabytes de datos.
No se puede crecer. El ingestor empieza el día con un fichero vacío y le añade 17 MB a lo largo de 24 horas. Con asignación contigua habría que declarar el tamaño final por adelantado, y si te quedas corto, la única salida es copiar el fichero entero a un hueco mayor.
Por eso la asignación contigua sobrevive solo donde el contenido es inmutable y se conoce de antemano: CD-ROM y DVD (ISO 9660), y los extents modernos, que son "contigüidad por trozos" y recuperan sus ventajas sin sus problemas.
Asignación enlazada y la tabla FAT
La alternativa opuesta: cada bloque guarda un puntero al siguiente, y el inodo solo apunta al primero. Los bloques pueden estar donde sea.
Resuelve de golpe los dos problemas anteriores: no hay fragmentación externa —cualquier bloque libre sirve— y crecer es trivial: se toma un bloque libre y se enlaza. Pero introduce otros dos, peores:
- El acceso aleatorio es O(n). Para leer el bloque 4.000 hay que leer los 4.000 anteriores, porque el puntero al siguiente está dentro de cada bloque. Un
lseekdeja de ser gratis. - El puntero roba espacio al bloque. Si el puntero ocupa 4 bytes, un bloque de 4.096 solo almacena 4.092 de datos, y las lecturas dejan de estar alineadas con las páginas.
La solución de FAT. El sistema de archivos de MS-DOS resolvió lo segundo sacando todos los punteros fuera de los bloques, a una tabla única: la File Allocation Table. Es un array con una entrada por cada bloque —llamado cluster en la terminología FAT— cuyo valor es el número del siguiente cluster del fichero, o un valor especial:
| Valor de la entrada | Significado |
|---|---|
0x00000000 |
Cluster libre |
2 … 0x0FFFFFEF |
Número del siguiente cluster de la cadena |
0x0FFFFFF7 |
Cluster defectuoso |
0x0FFFFFF8-0x0FFFFFFF |
Fin de la cadena (EOF) |
Un fichero que empieza en el cluster 100 se sigue leyendo FAT[100] = 250, FAT[250] = 251, FAT[251] = 999, FAT[999] = EOF. La entrada de directorio solo guarda el primer cluster.
Las mejoras sobre la lista enlazada pura son reales: los bloques de datos quedan enteros para datos, y si la FAT cabe en memoria, recorrer la cadena no cuesta accesos a disco. Pero los problemas de fondo siguen:
El acceso aleatorio sigue siendo O(n) en la tabla. Para llegar al cluster 4.000 hay que recorrer 4.000 entradas. Si la FAT está en RAM son 4.000 accesos a memoria —rápido pero no gratis—; si no cabe, son miles de accesos a disco.
La FAT crece con el volumen y hay que tenerla en RAM. Con clusters de 4 KiB, un volumen de 1 TB tiene 268.435.456 clusters, y a 4 bytes por entrada la tabla mide 1 GiB. Ese es el motivo real por el que FAT32 no se usa en volúmenes grandes: no es el límite de 4 GiB por fichero, es que la tabla de asignación se vuelve inmanejable.
La FAT es un punto único de fallo. Si se corrompe, se pierden todas las cadenas de todos los ficheros. Por eso FAT guarda dos copias y por eso chkdsk existe.
Y no hay diario. Un corte de luz mientras se actualiza la FAT deja cadenas rotas, clusters perdidos y enlaces cruzados: dos ficheros cuyas cadenas confluyen y comparten clusters, de modo que escribir en uno corrompe el otro.
Asignación indexada: el inodo y sus punteros indirectos
La solución de UNIX: reunir todos los punteros de un fichero en su propio inodo, en lugar de en una tabla global. Cada fichero tiene su índice, y solo se lee el índice del fichero que estás usando.
El problema inmediato es el tamaño. El inodo mide 256 bytes y reserva 60 bytes para punteros: con punteros de 4 bytes, caben 15. Con bloques de 4 KiB, eso son 60 KiB. Ridículo.
La solución clásica de UNIX es elegante: los quince punteros no son todos iguales.
| Puntero | Tipo | A qué apunta | Bloques que direcciona | Tamaño acumulado |
|---|---|---|---|---|
| 0-11 | Directos | Bloques de datos | 12 | 48 KiB |
| 12 | Indirecto simple | Un bloque de punteros | 1.024 | +4 MiB |
| 13 | Indirecto doble | Un bloque de punteros a bloques de punteros | 1.024² = 1.048.576 | +4 GiB |
| 14 | Indirecto triple | Tres niveles | 1.024³ = 1.073.741.824 | +4 TiB |
El cálculo, con bloques de 4 KiB y punteros de 4 bytes, es el que hay que saber hacer:
Punteros por bloque = 4096 B / 4 B = 1.024
Directos: 12 × 4 KiB = 48 KiB
Indirecto simple: 1.024 × 4 KiB = 4 MiB
Indirecto doble: 1.024 × 1.024 × 4 KiB = 4 GiB
Indirecto triple: 1.024 × 1.024 × 1.024 × 4 KiB = 4 TiB
─────────────
Tamaño máximo de fichero ≈ 4 TiB + 4 GiB + 4 MiB + 48 KiB ≈ 4,004 TiBLa belleza del esquema está en que el coste es proporcional al tamaño. Un fichero de 40 KiB usa solo punteros directos: leer cualquiera de sus bloques cuesta un acceso, porque la dirección está en el inodo que ya has leído. Solo los ficheros enormes pagan los tres niveles de indirección:
| Tamaño del fichero | Accesos extra para leer un bloque cualquiera |
|---|---|
| ≤ 48 KiB | 0 (puntero en el inodo) |
| ≤ 4 MiB | 1 |
| ≤ 4 GiB | 2 |
| ≤ 4 TiB | 3 |
Ahora, 2026-08-31.dat con este esquema. Necesita 4.219 bloques:
- Los 12 primeros van en los punteros directos.
- Los 1.024 siguientes, en el indirecto simple: acumulado 1.036.
- Los 3.183 restantes van en el indirecto doble, que necesita ⌈3.183 / 1.024⌉ = 4 bloques de segundo nivel.
Total de bloques de metadatos: 1 (indirecto simple) + 1 (raíz del doble) + 4 (segundo nivel) = 6 bloques, 24 KiB. Y leer el bloque 4.000 del fichero cuesta 3 accesos: raíz del doble, bloque de segundo nivel y, por fin, el dato.
Un detalle importante de implementación: el bloque de punteros con todo ceros representa un agujero. Así es como ext2/ext3 implementan los ficheros dispersos de 04-04 sin ninguna estructura adicional.
Extents: la solución moderna, medida sobre Meteora
Los punteros indirectos tienen un defecto de fondo: almacenan una dirección por bloque, aunque los bloques sean consecutivos. Para un fichero de 17 MB contiguo, guardan 4.219 números que van del 8.394.271 al 8.398.489, uno detrás de otro. Es una lista redundante.
Un extent es un rango contiguo descrito con tres números: bloque lógico de inicio, bloque físico de inicio y longitud. En ext4 ocupa 12 bytes:
struct ext4_extent {
__le32 ee_block; /* primer bloque LÓGICO que cubre este extent */
__le16 ee_len; /* cuántos bloques: hasta 32.768 = 128 MiB */
__le16 ee_start_hi; /* bloque FÍSICO, 16 bits altos */
__le32 ee_start_lo; /* bloque FÍSICO, 32 bits bajos */
};Los 60 bytes del inodo albergan una cabecera de 12 bytes más 4 extents. Si un fichero necesita más de 4, esos 60 bytes pasan a describir la raíz de un árbol de extents, con nodos internos en bloques aparte.
La comparación, sobre 2026-08-31.dat:
| Punteros indirectos (ext2/ext3) | Extents (ext4) | |
|---|---|---|
| Estructuras para describir 4.219 bloques | 4.219 punteros | 1 extent |
| Bloques de metadatos extra | 6 (24 KiB) | 0 |
| Bytes de descripción | 16.876 | 12 |
| Accesos para leer el bloque 4.000 | 3 | 0 |
| Accesos para leer el fichero entero | 4.219 + 6 | 1 petición secuencial |
De 16.876 bytes de metadatos a 12: una reducción de 1.400×. Y lo verificamos en 04-04:
$ sudo filefrag -v /var/lib/meteora/lecturas/2026-08-31.dat File size of ...2026-08-31.dat is 17280000 (4219 blocks of 4096 bytes) ext: logical_offset: physical_offset: length: expected: flags: 0: 0.. 4218: 8394271.. 8398489: 4219: last,eof /var/lib/meteora/lecturas/2026-08-31.dat: 1 extent found
Un extent para todo el fichero. Compara con un fichero fragmentado, que es lo que verías en un volumen lleno:
3.847 extents significa 3.847 saltos: en un HDD, 3.847 búsquedas de 8 ms cada una, más de 30 segundos solo en posicionar el cabezal.
XFS usa la misma idea con árboles B+ desde 1994, y sus extents llegan a 2 millones de bloques (8 GiB) cada uno. La tabla comparativa de las cuatro estrategias:
| Contigua | Enlazada / FAT | Indexada (inodos) | Extents | |
|---|---|---|---|---|
| Fragmentación externa | Sí, grave | No | No | Poca |
| Puede crecer | No | Sí | Sí | Sí |
| Acceso aleatorio | O(1) | O(n) | O(1) con 0-3 accesos | O(1) o O(log n) |
| Metadatos para 17 MB | 8 bytes | 16.876 B en la FAT global | 16.876 B + 6 bloques | 12 bytes |
| Ficheros dispersos | No | No | Sí | Sí |
| Usado hoy en | ISO 9660 | FAT32, exFAT | ext2, ext3 | ext4, XFS, Btrfs, NTFS |
Gestión del espacio libre
La otra mitad del problema: saber qué bloques están libres. Hay cuatro enfoques.
Mapa de bits. Un bit por bloque: 1 ocupado, 0 libre. Es lo que usa ext4, y lo vimos en 04-01.
- Tamaño: 1 bit por bloque de 4 KiB = 32 KiB de mapa por cada GiB, o 6,25 MiB para 200 GiB.
- Buscar un bloque libre: recorrer bits, acelerado con instrucciones de la CPU que examinan 64 a la vez.
- Ventaja decisiva: encontrar un rango contiguo es trivial —buscar N ceros seguidos—, y eso es justo lo que hace falta para asignar extents.
Lista enlazada de bloques libres. Cada bloque libre apunta al siguiente. Ocupa cero espacio adicional, pero no permite encontrar rangos contiguos: los bloques salen en el orden en que se liberaron, que es aleatorio. Es la razón de que un sistema con lista enlazada fragmente sin remedio.
Agrupación (grouping). Un bloque libre contiene las direcciones de los N siguientes bloques libres. Reduce los accesos a disco frente a la lista simple, pero sigue sin ayudar con la contigüidad.
Contadores / árboles. Guardar pares (primer bloque libre, cuántos consecutivos). Muy compacto cuando el espacio libre está poco fragmentado, y si se organiza en un árbol B+ indexado por longitud permite responder "dame 4.219 bloques seguidos" en O(log n). Es lo que hace XFS, con dos árboles B+ por grupo de asignación: uno ordenado por dirección y otro por tamaño.
| Método | Espacio | Buscar 1 bloque | Buscar N contiguos | Usado en |
|---|---|---|---|---|
| Mapa de bits | 32 KiB/GiB | O(n/64) | Bueno | ext4, NTFS |
| Lista enlazada | 0 | O(1) | Imposible | Sistemas antiguos |
| Agrupación | Baja | O(1) | Malo | Variantes de UNIX |
| Árboles B+ | Variable | O(log n) | Óptimo | XFS, Btrfs |
Fragmentación: por qué ext4 y XFS fragmentan poco
Vuelve la pregunta del principio: ¿cómo acaba contiguo un fichero escrito 24 bytes cada 125 milisegundos durante un día? La respuesta son tres mecanismos que trabajan juntos.
1. Asignación diferida (delayed allocation). Es el más importante y el más contraintuitivo. Cuando el ingestor hace write(), ext4 no asigna ningún bloque: solo marca la página como sucia en la caché (04-04) y anota cuánto espacio hará falta. La asignación real se pospone hasta que el núcleo va a volcar de verdad.
La consecuencia es enorme: cuando llega el momento de asignar, el sistema ya sabe cuántos bloques necesita el conjunto, en lugar de tener que decidir uno a uno sin saber si habrá más. En lugar de 4.219 decisiones aisladas, toma unas pocas decisiones informadas y pide rangos grandes.
2. Asignación multibloque (multiblock allocation). Con esa información, ext4 pide al asignador un rango contiguo del tamaño necesario de una vez. El mapa de bits, que es bueno buscando ceros consecutivos, se lo da. De ahí sale el extent único.
3. Grupos de bloques y preasignación. Los grupos de 128 MiB de 04-01 mantienen juntos el inodo y sus datos, y ext4 además preasigna especulativamente un margen detrás de un fichero que está creciendo, reservándolo para él. Si el fichero sigue creciendo, se extiende sobre su propia reserva en lugar de saltar a otro sitio; si no, la reserva se libera al cerrar.
Los tres juntos explican el resultado: el ingestor escribe durante 24 horas y ext4 va extendiendo un único extent, porque cada vez que necesita más espacio ya lo tenía reservado justo detrás.
Se comprueba el estado global de un volumen con:
$ sudo e2fsck -fn /dev/md0 | tail -3 /dev/md0: 1834/200000 files (0.4% non-contiguous), 10736521/52428800 blocks
0,4 % de ficheros no contiguos. Compara con FAT32 tras un año de uso, donde el 30-40 % es normal.
Por qué desfragmentar apenas tiene sentido hoy
Tres razones acumulativas:
- Los sistemas de archivos modernos no fragmentan mucho, por los tres mecanismos anteriores. Un ext4 con menos del 85 % de ocupación se mantiene por debajo del 2 % de ficheros no contiguos indefinidamente.
- En un SSD la fragmentación es casi irrelevante. No hay cabezal que mover: el acceso a cualquier página cuesta lo mismo (02-05). Lo que sí importa es el número de peticiones, y ahí un fichero con 3.847 extents sigue siendo peor que uno con 1, pero la diferencia es de un factor 2 o 3, no de 100.
- Desfragmentar un SSD lo desgasta. Mover 200 GB son 200 GB de ciclos de escritura consumidos para un beneficio marginal. Es activamente perjudicial.
El único caso en que sigue teniendo sentido es un HDD muy lleno con ficheros grandes muy fragmentados. Para eso existen e4defrag en ext4 y xfs_fsr en XFS, siempre en caliente y por ficheros concretos:
sudo filefrag /srv/backup/imagen-vm.qcow2 # 3847 extents
sudo e4defrag /srv/backup/imagen-vm.qcow2
sudo filefrag /srv/backup/imagen-vm.qcow2 # 12 extentsRegla práctica: mantén los volúmenes por debajo del 85 % de ocupación. Es infinitamente más eficaz que desfragmentar, porque un asignador que no encuentra huecos grandes fragmenta necesariamente. La fragmentación es un síntoma de volumen lleno, no una enfermedad propia.
El problema de la consistencia: tres escrituras y un corte de luz
Cambiamos de tema y de gravedad. Cuando el ingestor añade un bloque a 2026-08-31.dat, el sistema de archivos debe hacer tres escrituras en tres sitios distintos del disco:
- El bloque de datos, en la zona de datos.
- El mapa de bits de bloques, marcando ese bloque como ocupado.
- El inodo, con la nueva dirección (o el extent extendido) y el tamaño actualizado.
Estas tres escrituras no pueden ser atómicas: están en sitios distintos del dispositivo, y el disco solo garantiza atomicidad a nivel de sector. Un corte de luz puede ocurrir en cualquier punto intermedio, y cada combinación deja un estado distinto:
| Escrituras completadas | Estado resultante | Gravedad |
|---|---|---|
| Ninguna | Coherente: la operación no ocurrió | Ninguna |
| Solo datos | Coherente: bloque huérfano con basura, marcado libre | Ninguna |
| Datos + mapa de bits | Bloque marcado ocupado que no pertenece a nadie | Leve: espacio perdido |
| Datos + inodo | El inodo apunta a un bloque marcado como libre | GRAVE |
| Mapa de bits + inodo | El fichero "tiene" un bloque con datos antiguos o basura | Media |
| Solo inodo | Apunta a un bloque libre y sin datos | GRAVE |
| Las tres | Coherente y correcto | Ninguna |
Las dos filas marcadas como GRAVE lo son por la misma razón, y merece la pena entenderla bien: si el inodo apunta a un bloque que el mapa de bits considera libre, el asignador se lo dará al siguiente fichero que pida espacio. A partir de ese momento dos ficheros comparten un bloque físico, y escribir en uno corrompe el otro. Es el enlace cruzado, la peor corrupción posible en un sistema de archivos, porque es silenciosa y se propaga.
La fila "media" es también instructiva: el fichero crece de tamaño y su bloque nuevo contiene lo que hubiera antes en ese sitio del disco. Si ese bloque perteneció a /etc/shadow o a un fichero de otro usuario, acabas de filtrar datos ajenos dentro de tu fichero. Es un problema de seguridad, no solo de consistencia, y volverá a aparecer al elegir el modo del diario.
fsck y por qué su coste es inaceptable
La solución tradicional era reparar después. Al arrancar, si el superbloque estaba marcado como "sucio", se ejecutaba fsck (file system check), que recorre todo el sistema de archivos comprobando invariantes:
Fase de e2fsck |
Qué comprueba |
|---|---|
| 1. Inodos y bloques | Que cada bloque referenciado esté marcado ocupado y no pertenezca a dos inodos |
| 2. Estructura de directorios | Que cada entrada apunte a un inodo válido |
| 3. Conectividad | Que todo inodo en uso sea alcanzable desde /; los huérfanos van a lost+found |
| 4. Contadores de enlaces | Que i_nlink coincida con el número real de entradas |
| 5. Mapas de bits y resúmenes | Que los mapas y los contadores del superbloque cuadren |
Funciona, y su capacidad de reparación es real. El problema es el coste, que crece con el tamaño del volumen:
| Tamaño | Inodos típicos | Duración aproximada de fsck |
|---|---|---|
| 10 GiB | 655.000 | ~10 segundos |
200 GiB (/dev/md0) |
13.100.000 | 3-8 minutos |
| 2 TiB | 131.000.000 | 30-90 minutos |
| 20 TiB | 1.310.000.000 | 6-12 horas |
Un servidor que tarda ocho horas en arrancar tras un corte de luz no es aceptable. Y hay un problema añadido: fsck no puede recuperar la información perdida, solo devolver el sistema a un estado coherente. Ante un enlace cruzado su remedio es duplicar el bloque o descartarlo: el sistema queda coherente, pero uno de los dos ficheros está corrupto sin remedio. Coherencia no es corrección.
Ese coste es lo que motivó el diario. Con él, la recuperación tras un corte pasa de recorrer 13 millones de inodos a releer unos megabytes de diario: de minutos u horas a menos de un segundo.
El diario: transacción, confirmación, aplicación y recuperación
La idea viene de las bases de datos y se llama write-ahead logging: antes de modificar nada, anota en un registro secuencial lo que vas a hacer. Si el sistema cae, se relee ese registro y se completa o se descarta lo pendiente.
En ext4, el diario es un área reservada del propio sistema de archivos —típicamente 128 MiB— que se usa como buffer circular, gestionada por un subsistema llamado JBD2.
Los cuatro pasos de una transacción:
graph TB
A["<b>1. INICIO</b><br/>Se agrupan las escrituras de metadatos<br/>relacionadas en UNA transacción"] --> B
B["<b>2. ESCRITURA AL DIARIO</b><br/>Los bloques modificados se escriben<br/>en el área del diario, secuencialmente"] --> C
C["<b>3. CONFIRMACIÓN (commit)</b><br/>Se escribe el bloque de commit<br/>con su suma de verificación<br/>← ESTE ES EL PUNTO SIN RETORNO"] --> D
D["<b>4. APLICACIÓN (checkpoint)</b><br/>Los bloques se escriben en su<br/>posición DEFINITIVA, sin prisa"] --> E
E["<b>5. LIBERACIÓN</b><br/>El espacio del diario se reutiliza"]
Y ahora lo que ocurre si se corta la luz en cada punto:
| Momento del corte | ¿Hay bloque de commit? | Qué hace la recuperación | Resultado |
|---|---|---|---|
| Durante el paso 2 | No | Descartar la transacción incompleta | Como si no hubiera pasado |
| Justo antes del commit | No | Descartar | Como si no hubiera pasado |
| Justo después del commit | Sí | Repetir (replay): copiar del diario a su sitio | Operación completada |
| Durante el paso 4 | Sí | Repetir; escribir dos veces lo mismo es inocuo | Operación completada |
| Después del paso 5 | — | Nada que hacer | Ya estaba |
La clave de todo está en el bloque de commit y su suma de verificación:
El bloque de commit es atómico: se escribe entero o no se escribe. Su presencia y su suma correcta significan "esta transacción está completa en el diario". Su ausencia significa "esto no llegó a ocurrir".
Con eso, el sistema nunca puede quedar a medias. Las tres escrituras de nuestro ejemplo —dato, mapa de bits, inodo— se agrupan en una transacción, y tras la recuperación o están las tres o no está ninguna. La atomicidad que el disco no da se construye por software.
La recuperación en la práctica:
$ sudo dmesg | grep -i ext4 EXT4-fs (md0): recovery complete EXT4-fs (md0): mounted filesystem with ordered data mode. Opts: noatime
Ese recovery complete es la línea que aparece tras un corte, y aparece en menos de un segundo, porque solo hay que releer el diario. Se puede forzar la lectura del diario con sudo dumpe2fs /dev/md0 | grep -i journal, que confirma su tamaño y su estado.
Dos precisiones necesarias para no sobrevalorar el mecanismo:
- Escribir dos veces cuesta. Cada metadato se escribe al diario y luego a su sitio, lo que amplifica las escrituras. Por eso el diario se agrupa por lotes —muchas operaciones en una transacción— y se confirma cada pocos segundos (
commit=5por defecto), no en cada operación. - El diario protege la estructura, no necesariamente tus datos. Cuáles son exactamente sus garantías depende del modo, que es el apartado siguiente.
Los tres modos de ext4 y cuál elige Meteora
ext4 ofrece tres políticas sobre qué va al diario, y la diferencia entre ellas es muy real:
| Modo | Al diario van... | Orden garantizado | Coste | Riesgo tras un corte |
|---|---|---|---|---|
data=journal |
Metadatos y datos | Total | Alto: todo se escribe dos veces | Ninguno: datos y metadatos consistentes |
data=ordered |
Solo metadatos, pero los datos se escriben ANTES del commit | Datos antes que metadatos | Bajo | Se pierden datos no confirmados, pero nunca aparecen datos ajenos |
data=writeback |
Solo metadatos, sin ordenar | Ninguno | El más bajo | El fichero puede contener basura o datos de otro fichero |
El caso que separa ordered de writeback merece detalle, porque es exactamente la fila "media" de la tabla de estados del apartado 7.
Con writeback, el diario garantiza que el inodo quedará coherente: dirá que el fichero mide 17.280.024 bytes y apuntará al bloque nuevo. Pero no garantiza que los datos de ese bloque se hayan escrito. Tras un corte puedes encontrarte un fichero cuyo tamaño ha crecido y cuyo bloque final contiene lo que hubiera antes en ese sitio del disco: restos de un fichero borrado, quizá de otro usuario. El sistema de archivos está perfectamente coherente y fsck no ve nada raro, pero has leído datos que no eran tuyos. Es un problema de seguridad, no solo de integridad.
Con ordered, ext4 impone una regla simple: los bloques de datos nuevos se escriben en el disco antes de confirmar la transacción que los referencia. Así, si el commit está, los datos están. Si el commit no está, el fichero conserva su tamaño anterior y el bloque nunca fue suyo. Nunca puede aparecer contenido ajeno dentro de un fichero. Y todo eso sin escribir los datos dos veces: solo se ordenan.
Con data=journal los datos también pasan por el diario, lo que da la garantía más fuerte —el contenido de un write() confirmado sobrevive íntegro— a costa de escribir cada byte dos veces, con una penalización del 30-50 % en escritura. Curiosamente, puede ser más rápido en cargas de escrituras pequeñas y aleatorias, porque el diario es secuencial; pero para escritura continua es claramente peor.
Se consulta y se cambia así:
sudo dumpe2fs -h /dev/md0 | grep -i "default mount" # el modo por defecto
sudo tune2fs -o journal_data_ordered /dev/md0 # fijarlo en el superbloque
# o por montaje, en /etc/fstab: UUID=... /var/lib/meteora ext4 noatime,data=ordered 0 2Qué elige Meteora: data=ordered. El razonamiento completo:
writebackqueda descartado por seguridad./var/lib/meteoraes un volumen compartido con el archivo histórico; que un corte pueda dejar restos de bloques ajenos dentro de un fichero de lecturas es inaceptable, y además arruinaría el formato:meteo-apiinterpretaría esa basura como estructurasLecturacon temperaturas absurdas. Los datos meteorológicos corruptos son peores que los datos ausentes, porque nadie se da cuenta.data=journalno compensa. Su garantía adicional es que sobreviven los datos ya confirmados, pero eso ya lo controlamos desde la aplicación con elfdatasynccada 5 segundos de 04-04. Pagar un 30-50 % de rendimiento y el doble de desgaste del SSD para duplicar una garantía que ya tenemos no tiene sentido.orderedda exactamente lo que necesitamos: nunca aparecerá contenido ajeno, la estructura siempre queda coherente, la recuperación tarda menos de un segundo, y el coste sobrewritebackes de un pequeño porcentaje.
Y una nota que cierra el círculo con 04-04: el formato de Meteora ayuda mucho aquí. Como el fichero es una secuencia de registros de 24 bytes añadidos al final, un corte de luz deja como mucho un registro incompleto al final, que el lector detecta con tamaño % 24 != 0 y descarta. Un formato con estructura global —un índice al principio, o comprimido— no tendría esa propiedad. La elección del formato de datos es parte de la estrategia de integridad.
Alternativas al diario: copia al escribir y log-structured
El diario no es la única forma de sobrevivir a un corte.
Copia al escribir (copy-on-write, CoW), en Btrfs y ZFS. La idea es radical: nunca se sobrescribe un bloque vivo. Modificar un bloque significa escribir una copia nueva en un sitio libre, y luego actualizar el puntero que apuntaba al viejo. Pero ese puntero está en otro bloque, que tampoco se sobrescribe: se copia también, y así hasta la raíz del árbol. Al final, una sola escritura atómica del superbloque raíz hace visible todo el cambio de golpe.
| Diario (ext4, XFS) | Copia al escribir (Btrfs, ZFS) | |
|---|---|---|
| Sobrescribe datos vivos | Sí | Nunca |
| Escrituras dobles | Sí, de metadatos | No |
| Recuperación tras un corte | Releer el diario (< 1 s) | Instantánea: el estado anterior sigue ahí |
| Instantáneas | Externas (LVM, 04-03) | Nativas y casi gratis |
| Sumas de verificación de datos | No | Sí |
| Fragmentación con reescrituras aleatorias | Baja | Alta: cada cambio va a otro sitio |
| Madurez en Linux | Muy alta | Alta (Btrfs) / fuera del núcleo (ZFS) |
La fila de la fragmentación es la pega real de CoW y la razón de que no sea automáticamente la mejor opción: una base de datos que reescribe registros al azar fragmenta un sistema CoW mucho más que uno con diario. Por eso Btrfs ofrece chattr +C para desactivar el CoW en ficheros concretos.
Sistemas estructurados en registro (log-structured), como F2FS. Llevan la idea al extremo: todo el sistema de archivos es un registro secuencial. Cada escritura, sea de datos o de metadatos, se añade al final; nada se modifica en su sitio. Las escrituras aleatorias se convierten en secuenciales, que es exactamente lo que le conviene a la memoria flash (02-05), donde borrar es por bloques grandes y el desgaste se reparte mejor así. El precio es que hace falta un recolector de basura que compacte los bloques con datos obsoletos, y ese proceso compite con la carga real. Es la razón de que F2FS domine en móviles y tarjetas SD y apenas se use en servidores.
Integridad de datos: sumas de verificación y corrupción silenciosa
Aquí llegamos a la distinción que da nombre al último tercio de la lección, y que mucha gente no tiene clara:
El diario garantiza que la estructura del sistema de archivos sea coherente tras un corte. No garantiza que los datos que lees sean los que escribiste.
Son problemas distintos con causas distintas. La corrupción silenciosa (silent data corruption, bit rot) es un bit que cambia de valor sin que nadie lo pida, por causas físicas:
| Causa | Dónde ocurre | Frecuencia orientativa |
|---|---|---|
| Degradación del medio | Plato magnético, celda NAND | Aumenta con la edad |
| Rayos cósmicos y radiación | RAM sin ECC, buses | Continua, baja |
| Fallos del firmware del disco | Controladora | Rara pero real |
| Escrituras mal dirigidas | El disco escribe en el LBA equivocado | Rara, muy dañina |
| Escrituras perdidas | El disco confirma y no escribe | Rara, muy dañina |
| Cables o alimentación defectuosos | Bus SATA/SAS | Variable |
Las dos peores son las mal dirigidas y las perdidas, porque el disco cree que todo ha ido bien y no reporta ningún error. Los estudios clásicos de CERN y NetApp sobre millones de discos encontraron tasas del orden de un sector corrupto por cada 10¹⁴-10¹⁵ bits leídos: con 200 GiB releídos a diario, eso es un evento cada varios años por volumen. Poco, pero no cero, y sobre datos irrecuperables importa.
Qué protege cada capa:
| Capa | Detecta corrupción de... | Puede corregirla |
|---|---|---|
| ECC del disco | Errores dentro de un sector | Sí, hasta cierto punto |
| ECC de la RAM | Bits volteados en memoria | Sí (1 bit), detecta 2 |
metadata_csum de ext4 |
Metadatos e inodos | No, pero avisa |
| Diario de ext4 | Transacciones incompletas | Sí (repite o descarta) |
| RAID 1 | Discrepancias entre las dos copias | Detecta, pero ver abajo |
| Sumas de ZFS/Btrfs | Datos y metadatos | Sí, si hay redundancia |
El punto ciego del RAID 1
Este es el apartado que más sorprende, y afecta directamente a Meteora, cuyo /var/lib/meteora está sobre RAID 1 desde 02-05.
RAID 1 mantiene dos copias idénticas. Si un disco falla de forma visible —no responde, devuelve error de lectura— el sistema usa el otro y todo funciona: para eso está. Pero si un disco devuelve datos corruptos sin reportar error:
RAID 1 puede detectar que las dos copias difieren, pero no sabe cuál es la buena. No tiene ninguna información para decidirlo, porque no guarda sumas de verificación de los datos.
Peor aún: en funcionamiento normal, md lee de un solo disco por rendimiento —repartiendo peticiones entre ambos—, así que ni siquiera compara. La discrepancia solo se descubre si se hace un scrub explícito:
# Verificación semanal: lee ambos discos y compara cada bloque
echo check | sudo tee /sys/block/md0/md/sync_action
cat /proc/mdstat # progreso
cat /sys/block/md0/md/mismatch_cnt # discrepancias encontradasUn mismatch_cnt distinto de cero significa que las copias difieren. Y entonces llega el momento incómodo: el sistema no puede decirte cuál es la correcta. repair sincroniza copiando el primer disco sobre el segundo, lo que arregla la discrepancia... eligiendo al azar. Si el bueno era el segundo, acabas de propagar la corrupción a los dos.
Lo que aporta ZFS o Btrfs es precisamente eso: guardan una suma de verificación de cada bloque de datos en el nodo padre del árbol. Cuando leen un bloque, comprueban su suma; si no cuadra, saben que ese bloque está mal, van a la copia redundante, verifican su suma, y si es correcta la devuelven y reparan el original. Es el self-healing, y es cualitativamente distinto de lo que puede hacer RAID 1.
Cómo compensa Meteora este punto ciego, ya que eligió ext4 en 04-01:
metadata_csumactivado, que protege inodos, descriptores de grupo, mapas de bits y el diario. La estructura no se corromperá en silencio.- Scrub semanal del RAID con
check, y alerta simismatch_cntno es cero. No repara solo, pero avisa, que es lo mínimo. - CRC32 por registro en el propio formato. El
ingestorguarda una suma con cada lectura, y elagregadorla verifica. Es la capa que de verdad cierra el hueco: aunque el sistema de archivos y el RAID no vean nada, la aplicación detecta la lectura corrupta y la descarta. - Copias de seguridad verificadas, que son la última red.
El punto 3 es la lección general: cuando el almacenamiento no puede darte la garantía que necesitas, la aplicación puede. Un CRC de 4 bytes por registro de 24 encarece un 17 % el almacenamiento y convierte una corrupción silenciosa en un error detectado.
Barreras de escritura y discos que mienten
Queda un eslabón, y es el que puede invalidar todo lo anterior.
Todo el mecanismo del diario descansa en un orden: los bloques del diario deben llegar al medio antes que el bloque de commit, y este antes que los bloques definitivos. Pero entre el sistema de archivos y el plato hay una caché volátil en el propio disco: unos cientos de megabytes de DRAM donde el dispositivo acumula escrituras y las reordena para ser más eficiente.
Si el disco reordena y confirma antes de escribir, el commit puede llegar al medio antes que los datos que respalda. Un corte de luz en ese instante deja una transacción marcada como completa cuyos datos no existen, y la recuperación la aplicará con confianza. El diario habría empeorado las cosas.
La solución son las barreras de escritura (write barriers), órdenes explícitas al dispositivo:
| Mecanismo | Qué pide al disco |
|---|---|
| FLUSH CACHE | "Escribe al medio todo lo que tengas en caché antes de confirmarme" |
| FUA (Force Unit Access) | "Esta escritura concreta va al medio directamente, sin pasar por la caché" |
ext4 las usa por defecto (barrier=1). El coste es real —una barrera puede costar milisegundos— y por eso existe la opción barrier=0, que las desactiva.
Nunca montes con
barrier=0salvo que tengas una controladora RAID con batería o supercondensador que garantice el vaciado de su caché ante un corte. Sin eso, desactivar las barreras convierte el diario en un adorno.
Y el problema final, que no tiene solución por software: hay discos que mienten. Algunos dispositivos de consumo —y muchos pendrives y tarjetas SD baratas— ignoran la orden de vaciado y confirman inmediatamente, porque así puntúan mejor en los bancos de pruebas. Con uno de esos, todas las garantías se evaporan: el sistema de archivos cree haber impuesto un orden que el hardware no ha respetado.
sudo hdparm -W /dev/sda # ¿está activa la caché de escritura del disco?
sudo hdparm -W0 /dev/sda # desactivarla (más seguro, más lento)Que un disco respete las barreras no se puede comprobar desde el sistema operativo; requiere un banco de pruebas con corte de alimentación real. La consecuencia práctica es de compras y no de configuración: para datos que importan, usa discos con protección de pérdida de alimentación (power loss protection), que llevan condensadores para vaciar su caché al medio cuando se va la luz. Es la diferencia principal entre un SSD de consumo y uno de servidor, y explica buena parte de su precio.
Instantáneas y copias de seguridad: otra capa
Para cerrar, una distinción que se confunde constantemente y que conviene dejar clara:
| Mecanismo | Protege de... | No protege de... |
|---|---|---|
| Diario | Corte de luz, cuelgue | Fallo de disco, borrado, corrupción de datos |
| RAID 1 | Fallo completo de un disco | Borrado, corrupción silenciosa, incendio, rm -rf |
| Sumas de verificación | Corrupción silenciosa (la detectan) | Fallo de disco, borrado |
| Instantáneas (04-03) | Borrado accidental, mala actualización | Fallo del volumen que las contiene, incendio |
| Copias de seguridad | Casi todo, si están fuera de la máquina | Nada, si nunca se han restaurado |
Las tres ideas que hay que llevarse:
El RAID no es una copia de seguridad. Es alta disponibilidad: mantiene el servicio ante el fallo de un disco. Un rm -rf se replica a los dos discos instantáneamente, y un incendio se lleva los dos.
Una instantánea no es una copia de seguridad. Vive en el mismo volumen —o en el mismo grupo de volúmenes— que los datos originales. Si el volumen muere, mueren las dos. Su valor es otro: dar un punto consistente desde el que hacer la copia sin parar el servicio, que es exactamente para lo que la usamos en 04-03.
Una copia de seguridad no verificada no es una copia de seguridad. Restaurar de verdad, en otra máquina, cada cierto tiempo, es la única forma de saber que funciona. La estadística de copias que fallaron el día que hicieron falta es deprimente.
El esquema completo de Meteora, con cada capa cubriendo lo que la anterior no puede:
| Capa | Mecanismo | Qué cubre |
|---|---|---|
| 1 | ext4 data=ordered + metadata_csum |
Cortes de luz y cuelgues |
| 2 | RAID 1 sobre dos NVMe | Fallo de un disco, sin interrupción |
| 3 | Scrub semanal + mismatch_cnt |
Detección de discrepancias |
| 4 | CRC32 por registro en el formato | Corrupción silenciosa, a nivel de aplicación |
| 5 | Instantánea LVM nocturna (04-03) | Punto consistente para copiar |
| 6 | Copia cifrada fuera de la máquina | Borrado, incendio, cifrado por ransomware |
| 7 | Restauración de prueba trimestral | Que la capa 6 sirva de algo |
Ninguna de las siete es redundante, y ninguna sustituye a otra.
Errores Comunes y Consejos
Creer que el diario protege tus datos. En data=ordered, que es lo normal, el diario protege los metadatos. Un write() no confirmado con fsync se pierde igualmente en un corte, aunque el sistema de archivos quede impecable.
Montar con barrier=0 para ganar rendimiento. Sin una controladora con batería, eso desactiva la garantía de orden y convierte el diario en decoración. El rendimiento que ganas lo pagas la primera vez que se va la luz.
Confiar en writeback por ser el más rápido. Su riesgo no es solo perder datos: es que aparezca contenido de otros ficheros dentro de los tuyos, con la implicación de seguridad que eso tiene.
Creer que RAID 1 protege de la corrupción silenciosa. Detecta discrepancias solo si haces scrub, y aun así no sabe cuál copia es la buena. Si necesitas esa garantía, necesitas sumas de verificación de datos: ZFS, Btrfs o tu propia aplicación.
Desfragmentar un SSD. No aporta nada medible y consume ciclos de escritura. Si tienes fragmentación en un ext4, el problema casi siempre es que el volumen está por encima del 85 % lleno.
Llenar un volumen por encima del 90 %. El asignador deja de encontrar rangos contiguos, la fragmentación se dispara y el rendimiento cae en picado. Vigila el 85 % como umbral de aviso.
Confundir instantánea con copia de seguridad. Comparten destino con los datos originales. Si el volumen muere, no tienes nada.
Consejo: activa metadata_csum y haz scrub semanal del RAID. Son dos medidas de coste casi nulo que convierten una corrupción invisible en una alerta.
Consejo: diseña el formato de datos pensando en el corte. Un fichero de registros de tamaño fijo añadidos al final se recupera descartando el último registro incompleto. Un formato con índice global o comprimido, no. Y un CRC por registro cierra el hueco que el sistema de archivos no cubre.
Ejercicios
Ejercicio 1: calcular metadatos y tamaño máximo
Para un sistema de archivos con bloques de 8 KiB y punteros de 4 bytes, calcula: (a) cuántos punteros caben en un bloque; (b) el tamaño máximo de fichero con el esquema de 12 punteros directos, uno indirecto simple, uno doble y uno triple, mostrando la aportación de cada nivel; (c) cuántos bloques de metadatos y cuántos accesos extra necesita un fichero de 17.280.000 bytes con ese esquema; (d) lo mismo con extents de ext4, suponiendo que el fichero está en un solo tramo contiguo. Comenta qué cambia respecto al cálculo con bloques de 4 KiB de la lección.
Ejercicio 2: analizar la fragmentación real de tu sistema
En tu máquina, usa filefrag para analizar al menos diez ficheros de tamaños muy distintos —desde unos pocos KB hasta varios GB si tienes—, y construye una tabla con tamaño, número de extents y extents por GiB. Después ejecuta sudo e2fsck -fn sobre un sistema de archivos desmontado (o interpreta la línea non-contiguous de un df/dumpe2fs) y valora si tu volumen está fragmentado. Explica qué relación observas entre tamaño de fichero, ocupación del volumen y fragmentación, y decide razonadamente si desfragmentarías algo.
Ejercicio 3: elegir el modo del diario para tres cargas
Para cada una de estas tres cargas, elige entre data=journal, data=ordered y data=writeback, y justifica la decisión analizando qué se pierde y qué se arriesga en un corte de luz: (A) el volumen /var/lib/meteora de las lecturas; (B) un volumen de caché de imágenes regenerables, donde el rendimiento de escritura es lo único que importa; (C) un volumen de una base de datos financiera con transacciones. Para cada uno, indica además qué otras medidas de las vistas en la lección añadirías y por qué.
Soluciones
Solución 1
(a) Punteros por bloque = 8.192 / 4 = 2.048.
(b) Tamaño máximo:
| Nivel | Bloques direccionados | Espacio |
|---|---|---|
| 12 directos | 12 | 12 × 8 KiB = 96 KiB |
| Indirecto simple | 2.048 | 16 MiB |
| Indirecto doble | 2.048² = 4.194.304 | 32 GiB |
| Indirecto triple | 2.048³ = 8.589.934.592 | 64 TiB |
| Total ≈ 64,03 TiB |
Duplicar el tamaño de bloque multiplica el máximo por 16, no por 2: cada nivel de indirección aporta un factor 2 por el tamaño del bloque y otro factor 2 por caber el doble de punteros, y con tres niveles eso es 2⁴ = 16. Es un ejemplo bonito de crecimiento no lineal.
(c) 17.280.000 / 8.192 = 2.109,375 → 2.110 bloques. Los 12 primeros son directos; quedan 2.098, que caben enteros en el indirecto simple (2.048)... no del todo: 2.098 > 2.048, así que 2.048 van al indirecto simple y 50 al doble.
Bloques de metadatos: 1 (indirecto simple) + 1 (raíz del doble) + 1 (un bloque de segundo nivel para los 50) = 3 bloques = 24 KiB. Accesos extra: 1 para los bloques del indirecto simple, 2 para los 50 del doble.
Con bloques de 4 KiB eran 6 bloques de metadatos y hasta 3 accesos extra: el bloque mayor reduce a la mitad los metadatos y ahorra un nivel de indirección, a costa de más fragmentación interna (04-01). Recuerda que en Linux esto es teórico, porque el bloque no puede superar el tamaño de página.
(d) Con extents y un solo tramo contiguo: 1 extent de 12 bytes dentro del inodo, 0 bloques de metadatos y 0 accesos extra. Da igual que el bloque sea de 4 u 8 KiB: la contigüidad es lo que elimina el problema, no el tamaño del bloque.
Solución 2
for f in $(find ~ /var/log /usr/lib -maxdepth 3 -type f -size +1M 2>/dev/null | head -20); do
tam=$(stat -c %s "$f")
ext=$(filefrag "$f" 2>/dev/null | grep -o '[0-9]* extent' | cut -d' ' -f1)
[ -n "$ext" ] && printf "%12d %6s %s\n" "$tam" "$ext" "$f"
done | sort -rnResultados típicos en un ext4 sano al 60 % de ocupación:
| Tamaño | Extents | Extents por GiB | Comentario |
|---|---|---|---|
| 4,2 GB | 38 | 9 | Excelente para su tamaño |
| 1,1 GB | 12 | 11 | Excelente |
| 340 MB | 4 | 12 | Bien |
| 17 MB | 1 | 59 | Óptimo |
| 2 MB | 1 | — | Óptimo |
Y en un volumen al 94 % de ocupación, el mismo fichero de 4,2 GB puede salir con 900 extents.
Relaciones observadas. Los ficheros pequeños (< 128 MiB) casi siempre salen en un solo extent, porque un extent de ext4 llega a 128 MiB y la asignación diferida ve el fichero completo antes de asignar. Los grandes tienen varios, pero pocos: la fragmentación crece mucho más despacio que el tamaño. Y la variable determinante no es el tamaño sino la ocupación del volumen: por encima del 90 % el asignador deja de encontrar huecos grandes y la fragmentación se multiplica.
¿Desfragmentaría? Casi con seguridad no. En un SSD, nunca: no hay ganancia medible y sí desgaste. En un HDD, solo un fichero concreto muy fragmentado (miles de extents) que se lea secuencialmente a menudo, con e4defrag sobre ese fichero. Y la acción realmente útil sería liberar espacio hasta bajar del 85 %, que ataca la causa en lugar del síntoma.
Solución 3
(A) /var/lib/meteora → data=ordered. Es la decisión de la lección. writeback queda descartado porque un corte podría dejar bloques con contenido ajeno dentro de un fichero de lecturas, y meteo-api los interpretaría como estructuras Lectura válidas con valores absurdos: datos corruptos indetectables son peores que datos ausentes. data=journal duplicaría las escrituras con un 30-50 % de penalización para dar una garantía que ya se cubre desde la aplicación con fdatasync cada 5 segundos. Medidas adicionales: metadata_csum, scrub semanal del RAID, CRC32 por registro, y el formato de registros de tamaño fijo que permite descartar el último incompleto.
(B) Caché de imágenes regenerables → data=writeback. Aquí sí. El razonamiento de seguridad que descartaba writeback en (A) se aplica solo porque en (A) el contenido importa; si un fichero de caché queda con basura, se detecta al usarlo y se regenera, que es la definición de caché. Se gana el máximo rendimiento de escritura. Medidas adicionales, coherentes con la naturaleza del dato: no hacer copia de seguridad de este volumen; montarlo noatime,nosuid,nodev,noexec (04-03); considerar directamente tmpfs si cabe en RAM; y validar cada entrada al leerla —con una suma o un identificador de versión—, descartando y regenerando la que no cuadre. Una opción aún más agresiva sería mkfs.ext4 -O ^has_journal, sin diario: tras un corte habría que ejecutar fsck o simplemente reformatear, lo que en una caché es perfectamente aceptable.
(C) Base de datos financiera → data=ordered, no data=journal. Este es el caso que más engaña. La intuición dice "lo más seguro posible, data=journal", y es la respuesta equivocada por dos motivos. Primero, una base de datos seria ya tiene su propio diario —el WAL de PostgreSQL, el redo log de Oracle— y sincroniza con fsync en cada confirmación de transacción: el diario de datos del sistema de archivos duplicaría exactamente el mismo trabajo, escribiendo cada byte cuatro veces en total. Segundo, esa duplicación cuesta un 30-50 % de rendimiento en la parte del sistema más sensible a la latencia. data=ordered da la garantía estructural necesaria y deja la durabilidad transaccional donde debe estar: en la capa que entiende qué es una transacción.
Medidas adicionales para (C), que son donde de verdad se juega el asunto: barreras activadas y discos con protección de pérdida de alimentación, porque un disco que miente invalida el fsync en el que se apoya el WAL; RAM con ECC, ya que un bit volteado en memoria corrompe el dato antes de escribirlo y ninguna suma del sistema de archivos lo detecta; RAID con scrub; sumas de verificación a nivel de página, que PostgreSQL ofrece con data_checksums; y copias con restauración verificada, además de archivado continuo del WAL para poder recuperar a un instante concreto.
La lección transversal de los tres casos: la garantía se pone en la capa que tiene la información para darla, y duplicarla en varias capas cuesta rendimiento sin añadir seguridad.
Conclusión
Las cuatro estrategias de asignación responden a la misma pregunta con compromisos distintos. La contigua es óptima en lectura y en metadatos —ocho bytes describen cualquier fichero— pero muere por fragmentación externa y por no poder crecer, así que solo sobrevive en medios de solo lectura. La enlazada y su variante con tabla, FAT, eliminan la fragmentación externa a cambio de un acceso aleatorio O(n) y de una tabla que crece con el volumen: 1 GiB de FAT para 1 TB con clusters de 4 KiB, más un punto único de fallo y ningún diario. La indexada de UNIX reúne los punteros en el inodo y resuelve el problema del tamaño con indirección escalonada —12 directos, simple, doble y triple—, que con bloques de 4 KiB alcanza 4,004 TiB y hace que el coste crezca con el tamaño: 0 accesos extra hasta 48 KiB, 3 para los ficheros mayores. Y los extents describen rangos contiguos con 12 bytes: los 4.219 bloques de 2026-08-31.dat pasan de 16.876 bytes de punteros y 6 bloques de metadatos a un solo extent, verificado con filefrag.
Que ese fichero acabe contiguo pese a escribirse 24 bytes cada 125 ms durante un día se debe a tres mecanismos: asignación diferida, que no reserva nada hasta el volcado y por tanto decide sabiendo cuánto hace falta; asignación multibloque, que pide el rango entero de golpe a un mapa de bits bueno buscando ceros consecutivos; y grupos de bloques con preasignación, que reservan margen detrás de un fichero que crece. El resultado es un 0,4 % de ficheros no contiguos, y explica por qué desfragmentar apenas tiene sentido: no hace falta en ext4 sano, es irrelevante en SSD y además lo desgasta. La fragmentación es un síntoma de volumen lleno: mantén la ocupación por debajo del 85 %.
La segunda mitad ha sido sobre supervivencia. Añadir un bloque son tres escrituras —dato, mapa de bits, inodo— que el hardware no puede hacer atómicas, y de sus siete combinaciones dos son graves: cuando el inodo apunta a un bloque marcado como libre, el asignador se lo dará a otro fichero y aparecerá un enlace cruzado, la peor corrupción posible. fsck sabe reparar, pero 8 horas en un volumen de 20 TiB es inaceptable, y además solo devuelve la coherencia, no la corrección. El diario resuelve ambas cosas: transacción, escritura al diario, bloque de commit atómico con suma de verificación —el punto sin retorno— y aplicación diferida; tras un corte, la recuperación repite lo confirmado y descarta lo demás en menos de un segundo.
De los tres modos, writeback deja que aparezca contenido ajeno dentro de tus ficheros —un problema de seguridad, no solo de integridad—, data=journal escribe todo dos veces por un 30-50 % de coste, y data=ordered garantiza que los datos llegan al disco antes del commit que los referencia, sin duplicar nada. Meteora elige ordered, y su formato de registros de 24 bytes añadidos al final completa la estrategia: un corte deja como mucho un registro incompleto que el lector descarta con tamaño % 24. La elección del formato de datos es parte de la estrategia de integridad. Las alternativas al diario son la copia al escribir de Btrfs y ZFS —sin sobrescrituras, con instantáneas nativas y sumas de datos, a costa de fragmentar con reescrituras aleatorias— y los sistemas estructurados en registro como F2FS, que convierten toda escritura en secuencial a cambio de un recolector de basura.
Y la distinción final, la más importante de la lección: el diario garantiza la coherencia de la estructura, no la integridad de los datos. La corrupción silenciosa existe, y ext4 no la ve. RAID 1 puede detectar que las dos copias difieren pero no sabe cuál es la buena —y ni siquiera compara salvo que hagas scrub—, mientras que ZFS y Btrfs sí, porque guardan una suma por bloque de datos y pueden repararse solos. Meteora cierra ese hueco con metadata_csum, scrub semanal y, sobre todo, un CRC32 por registro en su propio formato: cuando el almacenamiento no puede darte la garantía que necesitas, la aplicación puede. Todo ello descansa en las barreras de escritura, que imponen el orden que el diario necesita, y que un disco que ignore el vaciado de su caché convierte en ficción: por eso los SSD de servidor llevan protección de pérdida de alimentación. Y las siete capas de Meteora —diario, RAID, scrub, CRC, instantánea, copia externa y restauración de prueba— cubren cada una lo que la anterior no puede: el RAID no es una copia de seguridad, la instantánea tampoco, y una copia sin restaurar no es nada.
Nos queda la última pregunta del módulo, y es de otra naturaleza. Hemos protegido /var/lib/meteora/lecturas/2026-08-31.dat del corte de luz, del fallo de un disco y hasta de un bit que se voltea en el plato. Pero llevamos cinco lecciones viendo -rw-r----- en cada ls -l y Uid: (990/meteora) en cada stat, y no hemos explicado qué significan. ¿Quién puede leer ese fichero, y quién puede borrarlo? ¿Por qué se puede borrar un fichero que no se puede leer? ¿Qué comprueba exactamente el núcleo, y en qué momento? ¿Y cómo consigue passwd modificar /etc/shadow, que solo root puede escribir, cuando lo ejecuta un usuario normal?
Es lo que veremos en Seguridad y Permisos de Archivos.
Fundamentos de Sistemas Operativos
Módulo 1: Introducción a los Sistemas Operativos
- Conceptos Básicos de Sistemas Operativos
- Historia y Evolución de los Sistemas Operativos
- Tipos de Sistemas Operativos
- Funciones Principales de un Sistema Operativo
- Arquitectura del Núcleo: Monolítico, Microkernel e Híbrido
- Modo Usuario, Modo Núcleo y Llamadas al Sistema
Módulo 2: Gestión de Recursos
- Gestión de Procesos
- Planificación de la CPU
- Gestión de Memoria
- Memoria Virtual y Paginación
- Gestión de Almacenamiento
- Gestión de Dispositivos
- Controladores, Interrupciones y Operaciones de E/S
Módulo 3: Concurrencia
- Conceptos de Concurrencia
- Hilos y Procesos
- Comunicación entre Procesos (IPC)
- Sincronización y Exclusión Mutua
- Problemas Clásicos de Concurrencia
- Interbloqueos: Prevención, Detección y Recuperación
Módulo 4: Estructuras de Archivos
- Sistemas de Archivos
- Estructuras de Directorios
- Particiones, Montaje y Sistema de Archivos Virtual
- Gestión de Archivos
- Asignación de Espacio, Journaling e Integridad
- Seguridad y Permisos de Archivos
Módulo 5: Protección y Seguridad del Sistema
- Principios de Protección y Control de Acceso
- Usuarios, Autenticación y Escalada de Privilegios
- Amenazas Comunes y Endurecimiento del Sistema
- Auditoría, Registros y Respuesta a Incidentes
Módulo 6: Virtualización y Contenedores
- Virtualización: Hipervisores y Máquinas Virtuales
- Contenedores: Namespaces y cgroups
- El Sistema Operativo en la Nube
- Sistemas Operativos Móviles y de Tiempo Real
