La lliçó anterior va acabar amb una dada que mereix explicació. filefrag ens va dir que els 4.219 blocs de 2026-08-31.dat són en un sol extent, perfectament contigus del bloc 8.394.271 al 8.398.489. I tanmateix aquell fitxer es va escriure 24 bytes cada 125 mil·lisegons durant 24 hores, mentre meteo-api, l'agregador, els registres del sistema i mitja dotzena de serveis més escrivien al mateix volum. Com acaba contigu un fitxer escrit així?

Aquesta és la primera meitat d'aquesta lliçó: com decideix un sistema de fitxers quins blocs ocupa un fitxer, com porta el compte dels que estan lliures, i com representa aquella llista dins d'un inode que només té 60 bytes per a punters. Recorrerem les quatre estratègies històriques —contigua, enllaçada, indexada i per extents— amb els números concrets de Meteora, i entendràs de passada per què FAT32 és lenta amb fitxers grans i per què desfragmentar ja no té sentit.

La segona meitat és més greu. Afegir un bloc a un fitxer no és una operació, en són tres: escriure la dada, marcar el bloc com a ocupat al mapa de bits, i actualitzar l'inode amb la seva adreça nova i la mida. Si es talla la llum entre aquelles tres escriptures, el sistema de fitxers queda en un estat incoherent que pot anar des d'una pèrdua d'espai innòcua fins a la corrupció creuada de dos fitxers. Veurem com el diari (journal) converteix tres operacions en una transacció atòmica, què garanteix exactament cadascun dels tres modes d'ext4, i per què la consistència de metadades no és el mateix que la integritat de les dades: un bit que es corromp al plat passa desapercebut per a ext4, per a XFS i per al RAID 1 de Meteora.

Contingut

  1. Assignació contigua: ràpida i fràgil
  2. Assignació enllaçada i la taula FAT
  3. Assignació indexada: l'inode i els seus punters indirectes
  4. Extents: la solució moderna, mesurada sobre Meteora
  5. Gestió de l'espai lliure
  6. Fragmentació: per què ext4 i XFS fragmenten poc
  7. El problema de la consistència: tres escriptures i un tall de llum
  8. fsck i per què el seu cost és inacceptable
  9. El diari: transacció, confirmació, aplicació i recuperació
  10. Els tres modes d'ext4 i quin tria Meteora
  11. Alternatives al diari: còpia en escriure i log-structured
  12. Integritat de dades: sumes de verificació i corrupció silenciosa
  13. Barreres d'escriptura i discos que menteixen
  14. Instantànies i còpies de seguretat: una altra capa

Assignació contigua: ràpida i fràgil

La idea més simple: cada fitxer ocupa un conjunt de blocs consecutius. L'inode només necessita guardar dos números, el bloc inicial i la longitud.

2026-08-31.dat  →  inici = 8.394.271,  longitud = 4.219

Els seus avantatges són notables. La lectura seqüencial és òptima: una sola petició d'E/S porta el fitxer sencer, sense buscar res. L'accés aleatori és trivial: el bloc del byte N és a inici + N/4096, una divisió. I les metadades són mínimes: vuit bytes descriuen un fitxer de qualsevol mida.

Però té dos problemes que la descarten per a ús general:

Fragmentació externa. En crear i esborrar fitxers de mides diferents, l'espai lliure queda trossejat en forats petits. Pots tenir 40 GB lliures i no poder crear un fitxer de 100 MB perquè el forat contigu més gran són 60 MB. És exactament la fragmentació externa que vam veure a Gestió de Memòria amb l'assignació per particions variables, i la solució seria igual de cara: compactar, és a dir, moure físicament gigabytes de dades.

No pot créixer. L'ingestor comença el dia amb un fitxer buit i li afegeix 17 MB al llarg de 24 hores. Amb assignació contigua caldria declarar la mida final per endavant, i si et quedes curt, l'única sortida és copiar el fitxer sencer a un forat més gran.

Per això l'assignació contigua sobreviu només on el contingut és immutable i es coneix per endavant: CD-ROM i DVD (ISO 9660), i els extents moderns, que són «contigüitat per trossos» i recuperen els seus avantatges sense els seus problemes.

Assignació enllaçada i la taula FAT

L'alternativa oposada: cada bloc guarda un punter al següent, i l'inode només apunta al primer. Els blocs poden ser on sigui.

Resol de cop els dos problemes anteriors: no hi ha fragmentació externa —qualsevol bloc lliure serveix— i créixer és trivial: s'agafa un bloc lliure i s'enllaça. Però n'introdueix dos altres, pitjors:

  • L'accés aleatori és O(n). Per llegir el bloc 4.000 cal llegir els 4.000 anteriors, perquè el punter al següent és dins de cada bloc. Un lseek deixa de ser gratis.
  • El punter roba espai al bloc. Si el punter ocupa 4 bytes, un bloc de 4.096 només emmagatzema 4.092 de dades, i les lectures deixen d'estar alineades amb les pàgines.

La solució de FAT. El sistema de fitxers d'MS-DOS va resoldre el segon punt traient tots els punters fora dels blocs, a una taula única: la File Allocation Table. És un array amb una entrada per cada bloc —anomenat cluster en la terminologia FAT— el valor del qual és el número del següent cluster del fitxer, o un valor especial:

Valor de l'entrada Significat
0x00000000 Cluster lliure
20x0FFFFFEF Número del següent cluster de la cadena
0x0FFFFFF7 Cluster defectuós
0x0FFFFFF8-0x0FFFFFFF Fi de la cadena (EOF)

Un fitxer que comença al cluster 100 se segueix llegint FAT[100] = 250, FAT[250] = 251, FAT[251] = 999, FAT[999] = EOF. L'entrada de directori només guarda el primer cluster.

Les millores sobre la llista enllaçada pura són reals: els blocs de dades queden sencers per a dades, i si la FAT cap a la memòria, recórrer la cadena no costa accessos a disc. Però els problemes de fons continuen:

L'accés aleatori continua sent O(n) a la taula. Per arribar al cluster 4.000 cal recórrer 4.000 entrades. Si la FAT és a la RAM són 4.000 accessos a memòria —ràpid però no gratis—; si no hi cap, són milers d'accessos a disc.

La FAT creix amb el volum i cal tenir-la a la RAM. Amb clusters de 4 KiB, un volum d'1 TB té 268.435.456 clusters, i a 4 bytes per entrada la taula fa 1 GiB. Aquest és el motiu real pel qual FAT32 no es fa servir en volums grans: no és el límit de 4 GiB per fitxer, és que la taula d'assignació es torna impossible de manejar.

La FAT és un punt únic de fallada. Si es corromp, es perden totes les cadenes de tots els fitxers. Per això FAT en guarda dues còpies i per això chkdsk existeix.

I no hi ha diari. Un tall de llum mentre s'actualitza la FAT deixa cadenes trencades, clusters perduts i enllaços creuats: dos fitxers les cadenes dels quals conflueixen i comparteixen clusters, de manera que escriure en un corromp l'altre.

Assignació indexada: l'inode i els seus punters indirectes

La solució d'UNIX: reunir tots els punters d'un fitxer al seu propi inode, en lloc d'en una taula global. Cada fitxer té el seu índex, i només es llegeix l'índex del fitxer que estàs fent servir.

El problema immediat és la mida. L'inode fa 256 bytes i reserva 60 bytes per a punters: amb punters de 4 bytes, n'hi caben 15. Amb blocs de 4 KiB, això són 60 KiB. Ridícul.

La solució clàssica d'UNIX és elegant: els quinze punters no són tots iguals.

Punter Tipus A què apunta Blocs que adreça Mida acumulada
0-11 Directes Blocs de dades 12 48 KiB
12 Indirecte simple Un bloc de punters 1.024 +4 MiB
13 Indirecte doble Un bloc de punters a blocs de punters 1.024² = 1.048.576 +4 GiB
14 Indirecte triple Tres nivells 1.024³ = 1.073.741.824 +4 TiB

El càlcul, amb blocs de 4 KiB i punters de 4 bytes, és el que cal saber fer:

Punters per bloc = 4096 B / 4 B = 1.024

Directes:            12 × 4 KiB                   =        48 KiB
Indirecte simple:  1.024 × 4 KiB                  =         4 MiB
Indirecte doble:   1.024 × 1.024 × 4 KiB          =         4 GiB
Indirecte triple:  1.024 × 1.024 × 1.024 × 4 KiB  =         4 TiB
                                                    ─────────────
Mida màxima de fitxer  ≈  4 TiB + 4 GiB + 4 MiB + 48 KiB  ≈  4,004 TiB

La bellesa de l'esquema és que el cost és proporcional a la mida. Un fitxer de 40 KiB fa servir només punters directes: llegir qualsevol dels seus blocs costa un accés, perquè l'adreça és a l'inode que ja has llegit. Només els fitxers enormes paguen els tres nivells d'indirecció:

Mida del fitxer Accessos extra per llegir un bloc qualsevol
≤ 48 KiB 0 (punter a l'inode)
≤ 4 MiB 1
≤ 4 GiB 2
≤ 4 TiB 3

Ara, 2026-08-31.dat amb aquest esquema. Necessita 4.219 blocs:

  • Els 12 primers van als punters directes.
  • Els 1.024 següents, a l'indirecte simple: acumulat 1.036.
  • Els 3.183 restants van a l'indirecte doble, que necessita ⌈3.183 / 1.024⌉ = 4 blocs de segon nivell.

Total de blocs de metadades: 1 (indirecte simple) + 1 (arrel del doble) + 4 (segon nivell) = 6 blocs, 24 KiB. I llegir el bloc 4.000 del fitxer costa 3 accessos: arrel del doble, bloc de segon nivell i, per fi, la dada.

Un detall important d'implementació: el bloc de punters amb tot zeros representa un forat. Així és com ext2/ext3 implementen els fitxers dispersos de 04-04 sense cap estructura addicional.

Extents: la solució moderna, mesurada sobre Meteora

Els punters indirectes tenen un defecte de fons: emmagatzemen una adreça per bloc, encara que els blocs siguin consecutius. Per a un fitxer de 17 MB contigu, guarden 4.219 números que van del 8.394.271 al 8.398.489, un darrere l'altre. És una llista redundant.

Un extent és un rang contigu descrit amb tres números: bloc lògic d'inici, bloc físic d'inici i longitud. A ext4 ocupa 12 bytes:

struct ext4_extent {
    __le32 ee_block;      /* primer bloc LÒGIC que cobreix aquest extent   */
    __le16 ee_len;        /* quants blocs: fins a 32.768 = 128 MiB         */
    __le16 ee_start_hi;   /* bloc FÍSIC, 16 bits alts                      */
    __le32 ee_start_lo;   /* bloc FÍSIC, 32 bits baixos                    */
};

Els 60 bytes de l'inode allotgen una capçalera de 12 bytes més 4 extents. Si un fitxer en necessita més de 4, aquells 60 bytes passen a descriure l'arrel d'un arbre d'extents, amb nodes interns en blocs a part.

La comparació, sobre 2026-08-31.dat:

Punters indirectes (ext2/ext3) Extents (ext4)
Estructures per descriure 4.219 blocs 4.219 punters 1 extent
Blocs de metadades extra 6 (24 KiB) 0
Bytes de descripció 16.876 12
Accessos per llegir el bloc 4.000 3 0
Accessos per llegir el fitxer sencer 4.219 + 6 1 petició seqüencial

De 16.876 bytes de metadades a 12: una reducció de 1.400×. I ho vam verificar a 04-04:

$ sudo filefrag -v /var/lib/meteora/lectures/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/lectures/2026-08-31.dat: 1 extent found

Un extent per a tot el fitxer. Compara-ho amb un fitxer fragmentat, que és el que veuries en un volum ple:

$ sudo filefrag /srv/backup/imatge-vm.qcow2
/srv/backup/imatge-vm.qcow2: 3847 extents found

3.847 extents signifiquen 3.847 salts: en un HDD, 3.847 cerques de 8 ms cadascuna, més de 30 segons només a posicionar el capçal.

XFS fa servir la mateixa idea amb arbres B+ des del 1994, i els seus extents arriben a 2 milions de blocs (8 GiB) cadascun. La taula comparativa de les quatre estratègies:

Contigua Enllaçada / FAT Indexada (inodes) Extents
Fragmentació externa Sí, greu No No Poca
Pot créixer No
Accés aleatori O(1) O(n) O(1) amb 0-3 accessos O(1) o O(log n)
Metadades per a 17 MB 8 bytes 16.876 B a la FAT global 16.876 B + 6 blocs 12 bytes
Fitxers dispersos No No
Fet servir avui a ISO 9660 FAT32, exFAT ext2, ext3 ext4, XFS, Btrfs, NTFS

Gestió de l'espai lliure

L'altra meitat del problema: saber quins blocs estan lliures. Hi ha quatre enfocaments.

Mapa de bits. Un bit per bloc: 1 ocupat, 0 lliure. És el que fa servir ext4, i ho vam veure a 04-01.

  • Mida: 1 bit per bloc de 4 KiB = 32 KiB de mapa per cada GiB, o 6,25 MiB per a 200 GiB.
  • Buscar un bloc lliure: recórrer bits, accelerat amb instruccions de la CPU que n'examinen 64 alhora.
  • Avantatge decisiu: trobar un rang contigu és trivial —buscar N zeros seguits—, i això és justament el que cal per assignar extents.

Llista enllaçada de blocs lliures. Cada bloc lliure apunta al següent. Ocupa zero espai addicional, però no permet trobar rangs contigus: els blocs surten en l'ordre en què es van alliberar, que és aleatori. És la raó que un sistema amb llista enllaçada fragmenti sense remei.

Agrupació (grouping). Un bloc lliure conté les adreces dels N blocs lliures següents. Redueix els accessos a disc enfront de la llista simple, però continua sense ajudar amb la contigüitat.

Comptadors / arbres. Guardar parells (primer bloc lliure, quants de consecutius). Molt compacte quan l'espai lliure està poc fragmentat, i si s'organitza en un arbre B+ indexat per longitud permet respondre «dona'm 4.219 blocs seguits» en O(log n). És el que fa XFS, amb dos arbres B+ per grup d'assignació: un ordenat per adreça i un altre per mida.

Mètode Espai Buscar 1 bloc Buscar N contigus Fet servir a
Mapa de bits 32 KiB/GiB O(n/64) Bo ext4, NTFS
Llista enllaçada 0 O(1) Impossible Sistemes antics
Agrupació Baixa O(1) Dolent Variants d'UNIX
Arbres B+ Variable O(log n) Òptim XFS, Btrfs

Fragmentació: per què ext4 i XFS fragmenten poc

Torna la pregunta del principi: com acaba contigu un fitxer escrit 24 bytes cada 125 mil·lisegons durant un dia? La resposta són tres mecanismes que treballen junts.

1. Assignació diferida (delayed allocation). És el més important i el més contraintuïtiu. Quan l'ingestor fa write(), ext4 no assigna cap bloc: només marca la pàgina com a bruta a la memòria cau (04-04) i anota quant espai caldrà. L'assignació real es posposa fins que el nucli va a abocar de debò.

La conseqüència és enorme: quan arriba el moment d'assignar, el sistema ja sap quants blocs necessita el conjunt, en lloc d'haver de decidir un a un sense saber si n'hi haurà més. En lloc de 4.219 decisions aïllades, pren unes poques decisions informades i demana rangs grans.

2. Assignació multibloc (multiblock allocation). Amb aquella informació, ext4 demana a l'assignador un rang contigu de la mida necessària d'una sola vegada. El mapa de bits, que és bo buscant zeros consecutius, l'hi dona. D'aquí surt l'extent únic.

3. Grups de blocs i preassignació. Els grups de 128 MiB de 04-01 mantenen junts l'inode i les seves dades, i ext4 a més preassigna especulativament un marge darrere d'un fitxer que està creixent, i l'hi reserva. Si el fitxer continua creixent, s'estén sobre la seva pròpia reserva en lloc de saltar a un altre lloc; si no, la reserva s'allibera en tancar.

Els tres junts expliquen el resultat: l'ingestor escriu durant 24 hores i ext4 va estenent un únic extent, perquè cada vegada que necessita més espai ja el tenia reservat just darrere.

Es comprova l'estat global d'un volum amb:

$ sudo e2fsck -fn /dev/md0 | tail -3
/dev/md0: 1834/200000 files (0.4% non-contiguous), 10736521/52428800 blocks

0,4 % de fitxers no contigus. Compara-ho amb FAT32 després d'un any d'ús, on el 30-40 % és normal.

Per què desfragmentar amb prou feines té sentit avui

Tres raons acumulatives:

  1. Els sistemes de fitxers moderns no fragmenten gaire, pels tres mecanismes anteriors. Un ext4 amb menys del 85 % d'ocupació es manté per sota del 2 % de fitxers no contigus indefinidament.
  2. En un SSD la fragmentació és gairebé irrellevant. No hi ha capçal per moure: l'accés a qualsevol pàgina costa el mateix (02-05). El que sí que importa és el nombre de peticions, i allà un fitxer amb 3.847 extents continua sent pitjor que un amb 1, però la diferència és d'un factor 2 o 3, no de 100.
  3. Desfragmentar un SSD el desgasta. Moure 200 GB són 200 GB de cicles d'escriptura consumits per a un benefici marginal. És activament perjudicial.

L'únic cas en què continua tenint sentit és un HDD molt ple amb fitxers grans molt fragmentats. Per a això existeixen e4defrag a ext4 i xfs_fsr a XFS, sempre en calent i per fitxers concrets:

sudo filefrag /srv/backup/imatge-vm.qcow2      # 3847 extents
sudo e4defrag /srv/backup/imatge-vm.qcow2
sudo filefrag /srv/backup/imatge-vm.qcow2      # 12 extents

Regla pràctica: mantén els volums per sota del 85 % d'ocupació. És infinitament més eficaç que desfragmentar, perquè un assignador que no troba forats grans fragmenta necessàriament. La fragmentació és un símptoma de volum ple, no una malaltia pròpia.

El problema de la consistència: tres escriptures i un tall de llum

Canviem de tema i de gravetat. Quan l'ingestor afegeix un bloc a 2026-08-31.dat, el sistema de fitxers ha de fer tres escriptures en tres llocs diferents del disc:

  1. El bloc de dades, a la zona de dades.
  2. El mapa de bits de blocs, marcant aquell bloc com a ocupat.
  3. L'inode, amb la nova adreça (o l'extent estès) i la mida actualitzada.

Aquestes tres escriptures no poden ser atòmiques: són en llocs diferents del dispositiu, i el disc només garanteix atomicitat a nivell de sector. Un tall de llum pot passar en qualsevol punt intermedi, i cada combinació deixa un estat diferent:

Escriptures completades Estat resultant Gravetat
Cap Coherent: l'operació no va passar Cap
Només dades Coherent: bloc orfe amb brossa, marcat lliure Cap
Dades + mapa de bits Bloc marcat ocupat que no pertany a ningú Lleu: espai perdut
Dades + inode L'inode apunta a un bloc marcat com a lliure GREU
Mapa de bits + inode El fitxer «té» un bloc amb dades antigues o brossa Mitjana
Només inode Apunta a un bloc lliure i sense dades GREU
Les tres Coherent i correcte Cap

Les dues files marcades com a GREU ho són per la mateixa raó, i val la pena entendre-la bé: si l'inode apunta a un bloc que el mapa de bits considera lliure, l'assignador el donarà al fitxer següent que demani espai. A partir d'aquell moment dos fitxers comparteixen un bloc físic, i escriure en un corromp l'altre. És l'enllaç creuat, la pitjor corrupció possible en un sistema de fitxers, perquè és silenciosa i es propaga.

La fila «mitjana» és també instructiva: el fitxer creix de mida i el seu bloc nou conté el que hi hagués abans en aquell lloc del disc. Si aquell bloc va pertànyer a /etc/shadow o a un fitxer d'un altre usuari, acabes de filtrar dades alienes dins del teu fitxer. És un problema de seguretat, no només de consistència, i tornarà a aparèixer en triar el mode del diari.

fsck i per què el seu cost és inacceptable

La solució tradicional era reparar després. En arrencar, si el superbloc estava marcat com a «brut», s'executava fsck (file system check), que recorre tot el sistema de fitxers comprovant invariants:

Fase d'e2fsck Què comprova
1. Inodes i blocs Que cada bloc referenciat estigui marcat ocupat i no pertanyi a dos inodes
2. Estructura de directoris Que cada entrada apunti a un inode vàlid
3. Connectivitat Que tot inode en ús sigui accessible des de /; els orfes van a lost+found
4. Comptadors d'enllaços Que i_nlink coincideixi amb el nombre real d'entrades
5. Mapes de bits i resums Que els mapes i els comptadors del superbloc quadrin

Funciona, i la seva capacitat de reparació és real. El problema és el cost, que creix amb la mida del volum:

Mida Inodes típics Durada aproximada de fsck
10 GiB 655.000 ~10 segons
200 GiB (/dev/md0) 13.100.000 3-8 minuts
2 TiB 131.000.000 30-90 minuts
20 TiB 1.310.000.000 6-12 hores

Un servidor que triga vuit hores a arrencar després d'un tall de llum no és acceptable. I hi ha un problema afegit: fsck no pot recuperar la informació perduda, només tornar el sistema a un estat coherent. Davant d'un enllaç creuat el seu remei és duplicar el bloc o descartar-lo: el sistema queda coherent, però un dels dos fitxers està corrupte sense remei. Coherència no és correcció.

Aquell cost és el que va motivar el diari. Amb ell, la recuperació després d'un tall passa de recórrer 13 milions d'inodes a rellegir uns quants megabytes de diari: de minuts o hores a menys d'un segon.

El diari: transacció, confirmació, aplicació i recuperació

La idea ve de les bases de dades i es diu write-ahead logging: abans de modificar res, anota en un registre seqüencial el que faràs. Si el sistema cau, es rellegeix aquell registre i es completa o es descarta el pendent.

A ext4, el diari és una àrea reservada del mateix sistema de fitxers —típicament 128 MiB— que es fa servir com a memòria intermèdia circular, gestionada per un subsistema anomenat JBD2.

Els quatre passos d'una transacció:

graph TB
    A["<b>1. INICI</b><br/>S'agrupen les escriptures de metadades<br/>relacionades en UNA transacció"] --> B
    B["<b>2. ESCRIPTURA AL DIARI</b><br/>Els blocs modificats s'escriuen<br/>a l'àrea del diari, seqüencialment"] --> C
    C["<b>3. CONFIRMACIÓ (commit)</b><br/>S'escriu el bloc de commit<br/>amb la seva suma de verificació<br/>← AQUEST ÉS EL PUNT SENSE RETORN"] --> D
    D["<b>4. APLICACIÓ (checkpoint)</b><br/>Els blocs s'escriuen a la seva<br/>posició DEFINITIVA, sense pressa"] --> E
    E["<b>5. ALLIBERAMENT</b><br/>L'espai del diari es reutilitza"]

I ara el que passa si es talla la llum a cada punt:

Moment del tall Hi ha bloc de commit? Què fa la recuperació Resultat
Durant el pas 2 No Descartar la transacció incompleta Com si no hagués passat
Just abans del commit No Descartar Com si no hagués passat
Just després del commit Repetir (replay): copiar del diari al seu lloc Operació completada
Durant el pas 4 Repetir; escriure dues vegades el mateix és innocu Operació completada
Després del pas 5 Res a fer Ja hi era

La clau de tot és el bloc de commit i la seva suma de verificació:

El bloc de commit és atòmic: s'escriu sencer o no s'escriu. La seva presència i la seva suma correcta signifiquen «aquesta transacció està completa al diari». La seva absència significa «això no va arribar a passar».

Amb això, el sistema no pot quedar mai a mitges. Les tres escriptures del nostre exemple —dada, mapa de bits, inode— s'agrupen en una transacció, i després de la recuperació o hi són les tres o no n'hi ha cap. L'atomicitat que el disc no dona es construeix per programari.

La recuperació a la pràctica:

$ sudo dmesg | grep -i ext4
EXT4-fs (md0): recovery complete
EXT4-fs (md0): mounted filesystem with ordered data mode. Opts: noatime

Aquell recovery complete és la línia que apareix després d'un tall, i apareix en menys d'un segon, perquè només cal rellegir el diari. Es pot forçar la lectura del diari amb sudo dumpe2fs /dev/md0 | grep -i journal, que en confirma la mida i l'estat.

Dues precisions necessàries per no sobrevalorar el mecanisme:

  • Escriure dues vegades costa. Cada metadada s'escriu al diari i després al seu lloc, cosa que amplifica les escriptures. Per això el diari s'agrupa per lots —moltes operacions en una transacció— i es confirma cada pocs segons (commit=5 per defecte), no a cada operació.
  • El diari protegeix l'estructura, no necessàriament les teves dades. Quines són exactament les seves garanties depèn del mode, que és l'apartat següent.

Els tres modes d'ext4 i quin tria Meteora

ext4 ofereix tres polítiques sobre què va al diari, i la diferència entre elles és molt real:

Mode Al diari hi van... Ordre garantit Cost Risc després d'un tall
data=journal Metadades i dades Total Alt: tot s'escriu dues vegades Cap: dades i metadades consistents
data=ordered Només metadades, però les dades s'escriuen ABANS del commit Dades abans que metadades Baix Es perden dades no confirmades, però mai no apareixen dades alienes
data=writeback Només metadades, sense ordenar Cap El més baix El fitxer pot contenir brossa o dades d'un altre fitxer

El cas que separa ordered de writeback mereix detall, perquè és exactament la fila «mitjana» de la taula d'estats de l'apartat 7.

Amb writeback, el diari garanteix que l'inode quedarà coherent: dirà que el fitxer fa 17.280.024 bytes i apuntarà al bloc nou. Però no garanteix que les dades d'aquell bloc s'hagin escrit. Després d'un tall pots trobar-te un fitxer la mida del qual ha crescut i el bloc final del qual conté el que hi hagués abans en aquell lloc del disc: restes d'un fitxer esborrat, potser d'un altre usuari. El sistema de fitxers està perfectament coherent i fsck no hi veu res estrany, però has llegit dades que no eren teves. És un problema de seguretat, no només d'integritat.

Amb ordered, ext4 imposa una regla simple: els blocs de dades nous s'escriuen al disc abans de confirmar la transacció que els referencia. Així, si el commit hi és, les dades hi són. Si el commit no hi és, el fitxer conserva la seva mida anterior i el bloc mai no va ser seu. Mai no pot aparèixer contingut aliè dins d'un fitxer. I tot això sense escriure les dades dues vegades: només s'ordenen.

Amb data=journal les dades també passen pel diari, cosa que dona la garantia més forta —el contingut d'un write() confirmat sobreviu íntegre— a costa d'escriure cada byte dues vegades, amb una penalització del 30-50 % en escriptura. Curiosament, pot ser més ràpid en càrregues d'escriptures petites i aleatòries, perquè el diari és seqüencial; però per a escriptura contínua és clarament pitjor.

Es consulta i es canvia així:

sudo dumpe2fs -h /dev/md0 | grep -i "default mount"   # el mode per defecte
sudo tune2fs -o journal_data_ordered /dev/md0          # fixar-lo al superbloc
# o per muntatge, a /etc/fstab:  UUID=...  /var/lib/meteora  ext4  noatime,data=ordered  0 2

Què tria Meteora: data=ordered. El raonament complet:

  1. writeback queda descartat per seguretat. /var/lib/meteora és un volum compartit amb l'arxiu històric; que un tall pugui deixar restes de blocs aliens dins d'un fitxer de lectures és inacceptable, i a més arruïnaria el format: meteo-api interpretaria aquella brossa com a estructures Lectura amb temperatures absurdes. Les dades meteorològiques corruptes són pitjors que les dades absents, perquè ningú no se n'adona.
  2. data=journal no compensa. La seva garantia addicional és que sobreviuen les dades ja confirmades, però això ja ho controlem des de l'aplicació amb el fdatasync cada 5 segons de 04-04. Pagar un 30-50 % de rendiment i el doble de desgast de l'SSD per duplicar una garantia que ja tenim no té sentit.
  3. ordered dona exactament el que necessitem: mai no apareixerà contingut aliè, l'estructura sempre queda coherent, la recuperació triga menys d'un segon, i el cost sobre writeback és d'un petit percentatge.

I una nota que tanca el cercle amb 04-04: el format de Meteora hi ajuda molt. Com que el fitxer és una seqüència de registres de 24 bytes afegits al final, un tall de llum deixa com a molt un registre incomplet al final, que el lector detecta amb mida % 24 != 0 i descarta. Un format amb estructura global —un índex al principi, o comprimit— no tindria aquella propietat. L'elecció del format de dades és part de l'estratègia d'integritat.

Alternatives al diari: còpia en escriure i log-structured

El diari no és l'única manera de sobreviure a un tall.

Còpia en escriure (copy-on-write, CoW), a Btrfs i ZFS. La idea és radical: mai no se sobreescriu un bloc viu. Modificar un bloc significa escriure'n una còpia nova en un lloc lliure, i després actualitzar el punter que apuntava al vell. Però aquell punter és en un altre bloc, que tampoc no se sobreescriu: també es copia, i així fins a l'arrel de l'arbre. Al final, una sola escriptura atòmica del superbloc arrel fa visible tot el canvi de cop.

Diari (ext4, XFS) Còpia en escriure (Btrfs, ZFS)
Sobreescriu dades vives Mai
Escriptures dobles Sí, de metadades No
Recuperació després d'un tall Rellegir el diari (< 1 s) Instantània: l'estat anterior continua allà
Instantànies Externes (LVM, 04-03) Natives i gairebé de franc
Sumes de verificació de dades No
Fragmentació amb reescriptures aleatòries Baixa Alta: cada canvi va a un altre lloc
Maduresa a Linux Molt alta Alta (Btrfs) / fora del nucli (ZFS)

La fila de la fragmentació és l'inconvenient real de CoW i la raó que no sigui automàticament la millor opció: una base de dades que reescriu registres a l'atzar fragmenta un sistema CoW molt més que un amb diari. Per això Btrfs ofereix chattr +C per desactivar el CoW en fitxers concrets.

Sistemes estructurats en registre (log-structured), com F2FS. Porten la idea a l'extrem: tot el sistema de fitxers és un registre seqüencial. Cada escriptura, sigui de dades o de metadades, s'afegeix al final; res no es modifica al seu lloc. Les escriptures aleatòries es converteixen en seqüencials, que és exactament el que li convé a la memòria flaix (02-05), on esborrar és per blocs grans i el desgast es reparteix millor així. El preu és que cal un recol·lector d'escombraries que compacti els blocs amb dades obsoletes, i aquell procés competeix amb la càrrega real. És la raó que F2FS domini en mòbils i targetes SD i amb prou feines es faci servir en servidors.

Integritat de dades: sumes de verificació i corrupció silenciosa

Aquí arribem a la distinció que dona nom a l'últim terç de la lliçó, i que molta gent no té clara:

El diari garanteix que l'estructura del sistema de fitxers sigui coherent després d'un tall. No garanteix que les dades que llegeixes siguin les que vas escriure.

Són problemes diferents amb causes diferents. La corrupció silenciosa (silent data corruption, bit rot) és un bit que canvia de valor sense que ningú ho demani, per causes físiques:

Causa On passa Freqüència orientativa
Degradació del mitjà Plat magnètic, cel·la NAND Augmenta amb l'edat
Raigs còsmics i radiació RAM sense ECC, busos Contínua, baixa
Fallades del microprogramari del disc Controladora Rara però real
Escriptures mal dirigides El disc escriu a l'LBA equivocat Rara, molt danyina
Escriptures perdudes El disc confirma i no escriu Rara, molt danyina
Cables o alimentació defectuosos Bus SATA/SAS Variable

Les dues pitjors són les mal dirigides i les perdudes, perquè el disc creu que tot ha anat bé i no reporta cap error. Els estudis clàssics del CERN i de NetApp sobre milions de discos van trobar taxes de l'ordre d'un sector corrupte per cada 10¹⁴-10¹⁵ bits llegits: amb 200 GiB rellegits diàriament, això és un esdeveniment cada uns quants anys per volum. Poc, però no zero, i sobre dades irrecuperables importa.

Què protegeix cada capa:

Capa Detecta corrupció de... Pot corregir-la
ECC del disc Errors dins d'un sector Sí, fins a cert punt
ECC de la RAM Bits capgirats a la memòria Sí (1 bit), en detecta 2
metadata_csum d'ext4 Metadades i inodes No, però avisa
Diari d'ext4 Transaccions incompletes Sí (repeteix o descarta)
RAID 1 Discrepàncies entre les dues còpies Detecta, però vegeu a sota
Sumes de ZFS/Btrfs Dades i metadades Sí, si hi ha redundància

El punt cec del RAID 1

Aquest és l'apartat que més sorprèn, i afecta directament Meteora, el /var/lib/meteora de la qual és sobre RAID 1 des de 02-05.

RAID 1 manté dues còpies idèntiques. Si un disc falla de manera visible —no respon, retorna error de lectura— el sistema fa servir l'altre i tot funciona: per a això hi és. Però si un disc retorna dades corruptes sense reportar error:

RAID 1 pot detectar que les dues còpies difereixen, però no sap quina és la bona. No té cap informació per decidir-ho, perquè no guarda sumes de verificació de les dades.

Pitjor encara: en funcionament normal, md llegeix d'un sol disc per rendiment —repartint peticions entre tots dos—, així que ni tan sols compara. La discrepància només es descobreix si es fa un scrub explícit:

# Verificació setmanal: llegeix tots dos discos i compara cada bloc
echo check | sudo tee /sys/block/md0/md/sync_action
cat /proc/mdstat                                     # progrés
cat /sys/block/md0/md/mismatch_cnt                   # discrepàncies trobades

Un mismatch_cnt diferent de zero significa que les còpies difereixen. I aleshores arriba el moment incòmode: el sistema no et pot dir quina és la correcta. repair sincronitza copiant el primer disc sobre el segon, cosa que arregla la discrepància... triant a l'atzar. Si el bo era el segon, acabes de propagar la corrupció a tots dos.

El que aporten ZFS o Btrfs és precisament això: guarden una suma de verificació de cada bloc de dades al node pare de l'arbre. Quan llegeixen un bloc, en comproven la suma; si no quadra, saben que aquell bloc està malament, van a la còpia redundant, en verifiquen la seva suma, i si és correcta la retornen i reparen l'original. És el self-healing, i és qualitativament diferent del que pot fer RAID 1.

Com compensa Meteora aquest punt cec, ja que va triar ext4 a 04-01:

  1. metadata_csum activat, que protegeix inodes, descriptors de grup, mapes de bits i el diari. L'estructura no es corromprà en silenci.
  2. Scrub setmanal del RAID amb check, i alerta si mismatch_cnt no és zero. No repara sol, però avisa, que és el mínim.
  3. CRC32 per registre en el mateix format. L'ingestor guarda una suma amb cada lectura, i l'agregador la verifica. És la capa que de debò tanca el forat: encara que el sistema de fitxers i el RAID no hi vegin res, l'aplicació detecta la lectura corrupta i la descarta.
  4. Còpies de seguretat verificades, que són l'última xarxa.

El punt 3 és la lliçó general: quan l'emmagatzematge no et pot donar la garantia que necessites, l'aplicació sí que pot. Un CRC de 4 bytes per registre de 24 encareix un 17 % l'emmagatzematge i converteix una corrupció silenciosa en un error detectat.

Barreres d'escriptura i discos que menteixen

Queda una baula, i és la que pot invalidar tot l'anterior.

Tot el mecanisme del diari descansa en un ordre: els blocs del diari han d'arribar al mitjà abans que el bloc de commit, i aquest abans que els blocs definitius. Però entre el sistema de fitxers i el plat hi ha una memòria cau volàtil al mateix disc: uns quants centenars de megabytes de DRAM on el dispositiu acumula escriptures i les reordena per ser més eficient.

Si el disc reordena i confirma abans d'escriure, el commit pot arribar al mitjà abans que les dades que avala. Un tall de llum en aquell instant deixa una transacció marcada com a completa les dades de la qual no existeixen, i la recuperació l'aplicarà amb confiança. El diari hauria empitjorat les coses.

La solució són les barreres d'escriptura (write barriers), ordres explícites al dispositiu:

Mecanisme Què demana al disc
FLUSH CACHE «Escriu al mitjà tot el que tinguis a la memòria cau abans de confirmar-me»
FUA (Force Unit Access) «Aquesta escriptura concreta va al mitjà directament, sense passar per la memòria cau»

ext4 les fa servir per defecte (barrier=1). El cost és real —una barrera pot costar mil·lisegons— i per això existeix l'opció barrier=0, que les desactiva.

No muntis mai amb barrier=0 llevat que tinguis una controladora RAID amb bateria o supercondensador que garanteixi el buidatge de la seva memòria cau davant d'un tall. Sense això, desactivar les barreres converteix el diari en un adorn.

I el problema final, que no té solució per programari: hi ha discos que menteixen. Alguns dispositius de consum —i molts llapis de memòria i targetes SD barates— ignoren l'ordre de buidatge i confirmen immediatament, perquè així puntuen millor als bancs de proves. Amb un d'aquests, totes les garanties s'evaporen: el sistema de fitxers creu haver imposat un ordre que el maquinari no ha respectat.

sudo hdparm -W /dev/sda        # està activa la memòria cau d'escriptura del disc?
sudo hdparm -W0 /dev/sda       # desactivar-la (més segur, més lent)

Que un disc respecti les barreres no es pot comprovar des del sistema operatiu; requereix un banc de proves amb tall d'alimentació real. La conseqüència pràctica és de compres i no de configuració: per a dades que importen, fes servir discos amb protecció de pèrdua d'alimentació (power loss protection), que porten condensadors per buidar la seva memòria cau al mitjà quan se'n va la llum. És la diferència principal entre un SSD de consum i un de servidor, i explica bona part del seu preu.

Instantànies i còpies de seguretat: una altra capa

Per tancar, una distinció que es confon constantment i que convé deixar clara:

Mecanisme Protegeix de... No protegeix de...
Diari Tall de llum, penjada Fallada de disc, esborrat, corrupció de dades
RAID 1 Fallada completa d'un disc Esborrat, corrupció silenciosa, incendi, rm -rf
Sumes de verificació Corrupció silenciosa (la detecten) Fallada de disc, esborrat
Instantànies (04-03) Esborrat accidental, mala actualització Fallada del volum que les conté, incendi
Còpies de seguretat Gairebé tot, si són fora de la màquina Res, si mai no s'han restaurat

Les tres idees que cal endur-se:

El RAID no és una còpia de seguretat. És alta disponibilitat: manté el servei davant de la fallada d'un disc. Un rm -rf es replica als dos discos instantàniament, i un incendi se'ls emporta tots dos.

Una instantània no és una còpia de seguretat. Viu al mateix volum —o al mateix grup de volums— que les dades originals. Si el volum mor, moren totes dues. El seu valor és un altre: donar un punt consistent des del qual fer la còpia sense aturar el servei, que és exactament per al que la vam fer servir a 04-03.

Una còpia de seguretat no verificada no és una còpia de seguretat. Restaurar de debò, en una altra màquina, cada cert temps, és l'única manera de saber que funciona. L'estadística de còpies que van fallar el dia que van caldre és depriment.

L'esquema complet de Meteora, amb cada capa cobrint el que l'anterior no pot:

Capa Mecanisme Què cobreix
1 ext4 data=ordered + metadata_csum Talls de llum i penjades
2 RAID 1 sobre dos NVMe Fallada d'un disc, sense interrupció
3 Scrub setmanal + mismatch_cnt Detecció de discrepàncies
4 CRC32 per registre al format Corrupció silenciosa, a nivell d'aplicació
5 Instantània LVM nocturna (04-03) Punt consistent per copiar
6 Còpia xifrada fora de la màquina Esborrat, incendi, xifratge per ransomware
7 Restauració de prova trimestral Que la capa 6 serveixi d'alguna cosa

Cap de les set no és redundant, i cap no substitueix una altra.

Errors Habituals i Consells

Creure que el diari protegeix les teves dades. A data=ordered, que és el normal, el diari protegeix les metadades. Un write() no confirmat amb fsync es perd igualment en un tall, encara que el sistema de fitxers quedi impecable.

Muntar amb barrier=0 per guanyar rendiment. Sense una controladora amb bateria, això desactiva la garantia d'ordre i converteix el diari en decoració. El rendiment que guanyes el pagues la primera vegada que se'n va la llum.

Confiar en writeback per ser el més ràpid. El seu risc no és només perdre dades: és que aparegui contingut d'altres fitxers dins dels teus, amb la implicació de seguretat que això té.

Creure que RAID 1 protegeix de la corrupció silenciosa. Detecta discrepàncies només si fas scrub, i tot i així no sap quina còpia és la bona. Si necessites aquella garantia, necessites sumes de verificació de dades: ZFS, Btrfs o la teva pròpia aplicació.

Desfragmentar un SSD. No aporta res mesurable i consumeix cicles d'escriptura. Si tens fragmentació en un ext4, el problema gairebé sempre és que el volum està per sobre del 85 % ple.

Omplir un volum per sobre del 90 %. L'assignador deixa de trobar rangs contigus, la fragmentació es dispara i el rendiment cau en picat. Vigila el 85 % com a llindar d'avís.

Confondre instantània amb còpia de seguretat. Comparteixen destinació amb les dades originals. Si el volum mor, no tens res.

Consell: activa metadata_csum i fes scrub setmanal del RAID. Són dues mesures de cost gairebé nul que converteixen una corrupció invisible en una alerta.

Consell: dissenya el format de dades pensant en el tall. Un fitxer de registres de mida fixa afegits al final es recupera descartant l'últim registre incomplet. Un format amb índex global o comprimit, no. I un CRC per registre tanca el forat que el sistema de fitxers no cobreix.

Exercicis

Exercici 1: calcular metadades i mida màxima

Per a un sistema de fitxers amb blocs de 8 KiB i punters de 4 bytes, calcula: (a) quants punters caben en un bloc; (b) la mida màxima de fitxer amb l'esquema de 12 punters directes, un indirecte simple, un de doble i un de triple, mostrant l'aportació de cada nivell; (c) quants blocs de metadades i quants accessos extra necessita un fitxer de 17.280.000 bytes amb aquell esquema; (d) el mateix amb extents d'ext4, suposant que el fitxer és en un sol tram contigu. Comenta què canvia respecte al càlcul amb blocs de 4 KiB de la lliçó.

Exercici 2: analitzar la fragmentació real del teu sistema

A la teva màquina, fes servir filefrag per analitzar almenys deu fitxers de mides molt diferents —des d'uns pocs KB fins a diversos GB si en tens—, i construeix una taula amb mida, nombre d'extents i extents per GiB. Després executa sudo e2fsck -fn sobre un sistema de fitxers desmuntat (o interpreta la línia non-contiguous d'un df/dumpe2fs) i valora si el teu volum està fragmentat. Explica quina relació observes entre mida de fitxer, ocupació del volum i fragmentació, i decideix raonadament si desfragmentaries alguna cosa.

Exercici 3: triar el mode del diari per a tres càrregues

Per a cadascuna d'aquestes tres càrregues, tria entre data=journal, data=ordered i data=writeback, i justifica la decisió analitzant què es perd i què s'arrisca en un tall de llum: (A) el volum /var/lib/meteora de les lectures; (B) un volum de memòria cau d'imatges regenerables, on el rendiment d'escriptura és l'única cosa que importa; (C) un volum d'una base de dades financera amb transaccions. Per a cadascun, indica a més quines altres mesures de les vistes a la lliçó hi afegiries i per què.

Solucions

Solució 1

(a) Punters per bloc = 8.192 / 4 = 2.048.

(b) Mida màxima:

Nivell Blocs adreçats Espai
12 directes 12 12 × 8 KiB = 96 KiB
Indirecte simple 2.048 16 MiB
Indirecte doble 2.048² = 4.194.304 32 GiB
Indirecte triple 2.048³ = 8.589.934.592 64 TiB
Total ≈ 64,03 TiB

Duplicar la mida de bloc multiplica el màxim per 16, no per 2: cada nivell d'indirecció aporta un factor 2 per la mida del bloc i un altre factor 2 per cabre-hi el doble de punters, i amb tres nivells això és 2⁴ = 16. És un exemple bonic de creixement no lineal.

(c) 17.280.000 / 8.192 = 2.109,375 → 2.110 blocs. Els 12 primers són directes; en queden 2.098, que caben sencers a l'indirecte simple (2.048)... no del tot: 2.098 > 2.048, així que 2.048 van a l'indirecte simple i 50 al doble.

Blocs de metadades: 1 (indirecte simple) + 1 (arrel del doble) + 1 (un bloc de segon nivell per als 50) = 3 blocs = 24 KiB. Accessos extra: 1 per als blocs de l'indirecte simple, 2 per als 50 del doble.

Amb blocs de 4 KiB eren 6 blocs de metadades i fins a 3 accessos extra: el bloc més gran redueix a la meitat les metadades i estalvia un nivell d'indirecció, a costa de més fragmentació interna (04-01). Recorda que a Linux això és teòric, perquè el bloc no pot superar la mida de pàgina.

(d) Amb extents i un sol tram contigu: 1 extent de 12 bytes dins de l'inode, 0 blocs de metadades i 0 accessos extra. Tant se val que el bloc sigui de 4 o de 8 KiB: la contigüitat és el que elimina el problema, no la mida del bloc.

Solució 2

for f in $(find ~ /var/log /usr/lib -maxdepth 3 -type f -size +1M 2>/dev/null | head -20); do
    mida=$(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" "$mida" "$ext" "$f"
done | sort -rn

Resultats típics en un ext4 sa al 60 % d'ocupació:

Mida Extents Extents per GiB Comentari
4,2 GB 38 9 Excel·lent per a la seva mida
1,1 GB 12 11 Excel·lent
340 MB 4 12
17 MB 1 59 Òptim
2 MB 1 Òptim

I en un volum al 94 % d'ocupació, el mateix fitxer de 4,2 GB pot sortir amb 900 extents.

Relacions observades. Els fitxers petits (< 128 MiB) gairebé sempre surten en un sol extent, perquè un extent d'ext4 arriba a 128 MiB i l'assignació diferida veu el fitxer complet abans d'assignar. Els grans en tenen uns quants, però pocs: la fragmentació creix molt més a poc a poc que la mida. I la variable determinant no és la mida sinó l'ocupació del volum: per sobre del 90 % l'assignador deixa de trobar forats grans i la fragmentació es multiplica.

Desfragmentaria? Gairebé amb seguretat no. En un SSD, mai: no hi ha guany mesurable i sí desgast. En un HDD, només un fitxer concret molt fragmentat (milers d'extents) que es llegeixi seqüencialment sovint, amb e4defrag sobre aquell fitxer. I l'acció realment útil seria alliberar espai fins a baixar del 85 %, que ataca la causa en lloc del símptoma.

Solució 3

(A) /var/lib/meteoradata=ordered. És la decisió de la lliçó. writeback queda descartat perquè un tall podria deixar blocs amb contingut aliè dins d'un fitxer de lectures, i meteo-api els interpretaria com a estructures Lectura vàlides amb valors absurds: dades corruptes indetectables són pitjors que dades absents. data=journal duplicaria les escriptures amb un 30-50 % de penalització per donar una garantia que ja es cobreix des de l'aplicació amb fdatasync cada 5 segons. Mesures addicionals: metadata_csum, scrub setmanal del RAID, CRC32 per registre, i el format de registres de mida fixa que permet descartar l'últim incomplet.

(B) Memòria cau d'imatges regenerables → data=writeback. Aquí sí. El raonament de seguretat que descartava writeback a (A) s'aplica només perquè a (A) el contingut importa; si un fitxer de memòria cau queda amb brossa, es detecta en fer-lo servir i es regenera, que és la definició de memòria cau. Es guanya el màxim rendiment d'escriptura. Mesures addicionals, coherents amb la naturalesa de la dada: no fer còpia de seguretat d'aquest volum; muntar-lo noatime,nosuid,nodev,noexec (04-03); considerar directament tmpfs si cap a la RAM; i validar cada entrada en llegir-la —amb una suma o un identificador de versió—, descartant i regenerant la que no quadri. Una opció encara més agressiva seria mkfs.ext4 -O ^has_journal, sense diari: després d'un tall caldria executar fsck o simplement reformatar, cosa que en una memòria cau és perfectament acceptable.

(C) Base de dades financera → data=ordered, no data=journal. Aquest és el cas que més enganya. La intuïció diu «el més segur possible, data=journal», i és la resposta equivocada per dos motius. Primer, una base de dades seriosa ja té el seu propi diari —el WAL de PostgreSQL, el redo log d'Oracle— i sincronitza amb fsync a cada confirmació de transacció: el diari de dades del sistema de fitxers duplicaria exactament la mateixa feina, escrivint cada byte quatre vegades en total. Segon, aquella duplicació costa un 30-50 % de rendiment a la part del sistema més sensible a la latència. data=ordered dona la garantia estructural necessària i deixa la durabilitat transaccional on ha d'estar: a la capa que entén què és una transacció.

Mesures addicionals per a (C), que són on de debò es juga l'assumpte: barreres activades i discos amb protecció de pèrdua d'alimentació, perquè un disc que menteix invalida el fsync en què s'apuntala el WAL; RAM amb ECC, ja que un bit capgirat a la memòria corromp la dada abans d'escriure-la i cap suma del sistema de fitxers no ho detecta; RAID amb scrub; sumes de verificació a nivell de pàgina, que PostgreSQL ofereix amb data_checksums; i còpies amb restauració verificada, a més d'arxivament continu del WAL per poder recuperar a un instant concret.

La lliçó transversal dels tres casos: la garantia es posa a la capa que té la informació per donar-la, i duplicar-la en diverses capes costa rendiment sense afegir seguretat.

Conclusió

Les quatre estratègies d'assignació responen a la mateixa pregunta amb compromisos diferents. La contigua és òptima en lectura i en metadades —vuit bytes descriuen qualsevol fitxer— però mor per fragmentació externa i per no poder créixer, així que només sobreviu en mitjans de només lectura. L'enllaçada i la seva variant amb taula, FAT, eliminen la fragmentació externa a canvi d'un accés aleatori O(n) i d'una taula que creix amb el volum: 1 GiB de FAT per a 1 TB amb clusters de 4 KiB, més un punt únic de fallada i cap diari. La indexada d'UNIX reuneix els punters a l'inode i resol el problema de la mida amb indirecció esglaonada —12 directes, simple, doble i triple—, que amb blocs de 4 KiB arriba a 4,004 TiB i fa que el cost creixi amb la mida: 0 accessos extra fins a 48 KiB, 3 per als fitxers més grans. I els extents descriuen rangs contigus amb 12 bytes: els 4.219 blocs de 2026-08-31.dat passen de 16.876 bytes de punters i 6 blocs de metadades a un sol extent, verificat amb filefrag.

Que aquell fitxer acabi contigu tot i escriure's 24 bytes cada 125 ms durant un dia es deu a tres mecanismes: assignació diferida, que no reserva res fins a l'abocament i per tant decideix sabent quant cal; assignació multibloc, que demana el rang sencer de cop a un mapa de bits bo buscant zeros consecutius; i grups de blocs amb preassignació, que reserven marge darrere d'un fitxer que creix. El resultat és un 0,4 % de fitxers no contigus, i explica per què desfragmentar amb prou feines té sentit: no cal en un ext4 sa, és irrellevant en SSD i a més el desgasta. La fragmentació és un símptoma de volum ple: mantén l'ocupació per sota del 85 %.

La segona meitat ha estat sobre supervivència. Afegir un bloc són tres escriptures —dada, mapa de bits, inode— que el maquinari no pot fer atòmiques, i de les seves set combinacions dues són greus: quan l'inode apunta a un bloc marcat com a lliure, l'assignador el donarà a un altre fitxer i apareixerà un enllaç creuat, la pitjor corrupció possible. fsck sap reparar, però 8 hores en un volum de 20 TiB és inacceptable, i a més només retorna la coherència, no la correcció. El diari resol totes dues coses: transacció, escriptura al diari, bloc de commit atòmic amb suma de verificació —el punt sense retorn— i aplicació diferida; després d'un tall, la recuperació repeteix el confirmat i descarta la resta en menys d'un segon.

Dels tres modes, writeback deixa que aparegui contingut aliè dins dels teus fitxers —un problema de seguretat, no només d'integritat—, data=journal escriu tot dues vegades per un 30-50 % de cost, i data=ordered garanteix que les dades arriben al disc abans del commit que les referencia, sense duplicar res. Meteora tria ordered, i el seu format de registres de 24 bytes afegits al final completa l'estratègia: un tall deixa com a molt un registre incomplet que el lector descarta amb mida % 24. L'elecció del format de dades és part de l'estratègia d'integritat. Les alternatives al diari són la còpia en escriure de Btrfs i ZFS —sense sobreescriptures, amb instantànies natives i sumes de dades, a costa de fragmentar amb reescriptures aleatòries— i els sistemes estructurats en registre com F2FS, que converteixen tota escriptura en seqüencial a canvi d'un recol·lector d'escombraries.

I la distinció final, la més important de la lliçó: el diari garanteix la coherència de l'estructura, no la integritat de les dades. La corrupció silenciosa existeix, i ext4 no la veu. RAID 1 pot detectar que les dues còpies difereixen però no sap quina és la bona —i ni tan sols compara llevat que facis scrub—, mentre que ZFS i Btrfs sí, perquè guarden una suma per bloc de dades i es poden reparar sols. Meteora tanca aquell forat amb metadata_csum, scrub setmanal i, sobretot, un CRC32 per registre en el seu propi format: quan l'emmagatzematge no et pot donar la garantia que necessites, l'aplicació sí que pot. Tot això descansa en les barreres d'escriptura, que imposen l'ordre que el diari necessita, i que un disc que ignori el buidatge de la seva memòria cau converteix en ficció: per això els SSD de servidor porten protecció de pèrdua d'alimentació. I les set capes de Meteora —diari, RAID, scrub, CRC, instantània, còpia externa i restauració de prova— cobreixen cadascuna el que l'anterior no pot: el RAID no és una còpia de seguretat, la instantània tampoc, i una còpia sense restaurar no és res.

Ens queda l'última pregunta del mòdul, i és d'una altra naturalesa. Hem protegit /var/lib/meteora/lectures/2026-08-31.dat del tall de llum, de la fallada d'un disc i fins i tot d'un bit que es capgira al plat. Però portem cinc lliçons veient -rw-r----- a cada ls -l i Uid: (990/meteora) a cada stat, i no hem explicat què signifiquen. Qui pot llegir aquell fitxer, i qui el pot esborrar? Per què es pot esborrar un fitxer que no es pot llegir? Què comprova exactament el nucli, i en quin moment? I com aconsegueix passwd modificar /etc/shadow, que només root pot escriure, quan l'executa un usuari normal?

És el que veurem a Seguretat i Permisos de Fitxers.

Fonaments de Sistemes Operatius

Mòdul 1: Introducció als Sistemes Operatius

Mòdul 2: Gestió de Recursos

Mòdul 3: Concurrència

Mòdul 4: Estructures de Fitxers

Mòdul 5: Protecció i Seguretat del Sistema

Mòdul 6: Virtualització i Contenidors

Mòdul 7: Administració i Diagnòstic a la Pràctica

© Copyright 2026. Tots els drets reservats