La lliçó anterior va acabar amb un cap solt deliberat. Sabem que l'inode 1180934 és el fitxer de lectures del 31 d'agost: guarda la seva mida, el seu propietari, les seves dates i els seus blocs. I sabem que no guarda el seu nom. Però aleshores, quan escrius cat /var/lib/meteora/lectures/2026-08-31.dat, qui converteix aquesta cadena de 43 caràcters en el número 1180934?
La resposta és el directori, i mereix una lliçó sencera per tres raons. La primera és que el seu disseny explica comportaments que desconcerten tothom: per què esborrar un fitxer es diu «desenllaçar», per què un fitxer esborrat continua ocupant 17 GB, per què un enllaç simbòlic es trenca i un de dur no, i per què no pots enllaçar durament un directori. La segona és que la resolució de camins és una de les operacions més executades de tot el sistema —milions de vegades per segon en un servidor amb càrrega— i entendre'n el cost explica decisions de disseny reals. I la tercera és que l'organització dels noms és un problema clàssic d'estructures de dades amb conseqüències mesurables: un directori amb 100.000 fitxers de lectures es comporta de manera radicalment diferent segons com estigui organitzat per dins.
Anem a obrir un directori i veure què hi ha a dins, recórrer l'evolució històrica de les organitzacions fins a arribar a l'arbre d'UNIX, seguir pas a pas la conversió d'un camí en un inode comptant els accessos a disc, distingir amb precisió els dos tipus d'enllaç, mesurar el cost de buscar en directoris enormes, i situar els camins de Meteora a l'estàndard FHS.
Contingut
- El directori és un fitxer que conté parells (nom, inode)
- Evolució de les organitzacions de directoris
- Camins absoluts i relatius,
.i.. - El directori de treball:
chdir,getcwdi per què és per procés - Resolució de camins pas a pas, amb el cost comptat
- La memòria cau de dentries: per què això no costa el que sembla
- Enllaços durs: un inode, diversos noms
- Enllaços simbòlics: un fitxer que conté un camí
- Organització interna i el cost dels directoris enormes
- L'estàndard FHS i per què Meteora és on és
- Esborrat: què fa realment
unlink
El directori és un fitxer que conté parells (nom, inode)
Comencem per l'afirmació central, que sol sonar estranya la primera vegada:
Un directori és un fitxer normal el contingut del qual és una taula de parells (nom, número d'inode). La seva única particularitat és que el tipus del seu inode és
d, i que el nucli es reserva el dret d'escriure'l ell mateix.
Té el seu inode, la seva mida, els seus permisos, les seves dates i els seus blocs de dades, exactament igual que 2026-08-31.dat. El que canvia és què hi ha dins d'aquests blocs.
Anem a demostrar-ho. Primer, el directori té mida i blocs com qualsevol fitxer:
$ stat /var/lib/meteora/lectures Fitxer: /var/lib/meteora/lectures Mida: 4096 Blocs: 8 Bloc d'E/S: 4096 directori Dispositiu: 9,0 Node-i: 1180929 Enllaços: 3 Accés: (0750/drwxr-x---) Uid: (990/meteora) Gid: (990/meteora)
4.096 bytes, 8 blocs de 512 B, inode 1180929. És un fitxer. Ara provem de llegir-lo com a tal:
Falla, i no perquè no hi hagi contingut, sinó perquè des del 1988 Linux prohibeix read() sobre un directori: si cada programa interpretés el format binari a mà, canviar l'estructura interna d'ext4 trencaria mig sistema. En el seu lloc hi ha una crida al sistema específica, getdents64(), que retorna les entrades en un format independent del sistema de fitxers. És la mateixa idea d'indirecció que veurem com a VFS a Particions, Muntatge i Sistema de Fitxers Virtual.
Amb strace es veu que ls la fa servir:
$ strace -e trace=openat,getdents64 ls /var/lib/meteora/lectures openat(AT_FDCWD, "/var/lib/meteora/lectures", O_RDONLY|O_NONBLOCK|O_DIRECTORY|O_CLOEXEC) = 3 getdents64(3, 0x5586a2f1b0d0 /* 8 entries */, 32768) = 240 getdents64(3, 0x5586a2f1b0d0 /* 0 entries */, 32768) = 0
ls obre el directori amb O_DIRECTORY, demana les seves entrades amb getdents64 —vuit de cop, 240 bytes— i torna a cridar fins a rebre 0, que significa «no n'hi ha més». Tot ls, tot find i tot os.listdir() acaben aquí.
Per veure el contingut cru cal debugfs:
$ sudo debugfs -R "ls -l /var/lib/meteora/lectures" /dev/md0 1180929 40750 (2) 990 990 4096 1-Sep-2026 00:00 . 1180928 40750 (2) 990 990 4096 1-Sep-2026 00:00 .. 1180934 100640 (1) 990 990 17280000 31-Aug-2026 23:59 2026-08-31.dat 1180935 100640 (1) 990 990 17280000 1-Sep-2026 12:40 2026-09-01.dat 1180936 120777 (7) 990 990 14 1-Sep-2026 00:00 avui.dat 1180940 40750 (2) 990 990 4096 1-Sep-2026 00:00 arxiu
Aquí hi ha la taula: sis parells (nom, inode). La primera columna és el número d'inode i l'última el nom; entremig, el mode en octal amb el tipus davant (40 directori, 100 regular, 120 enllaç simbòlic).
El format real d'una entrada a ext4, que és d'una senzillesa gairebé decebedora:
struct ext4_dir_entry_2 {
__le32 inode; /* 4 B: número d'inode, 0 = entrada esborrada */
__le16 rec_len; /* 2 B: longitud TOTAL d'aquesta entrada */
__u8 name_len; /* 1 B: longitud del nom (màx. 255) */
__u8 file_type; /* 1 B: 1=regular 2=dir 7=symlink... */
char name[]; /* el nom, sense terminador nul */
};Vuit bytes de capçalera més el nom, arrodonit a múltiple de 4. L'entrada de 2026-08-31.dat (14 caràcters) ocupa 8 + 14 = 22 → 24 bytes. Dos detalls amb conseqüències:
rec_lenés la longitud total, no la del nom. Recórrer el directori consisteix a avançarrec_lenbytes des de cada entrada. Quan s'esborra una entrada, el sistema no mou res: simplement suma el seurec_lenal de l'entrada anterior, que se l'«empassa». Per això un directori que va tenir un milió de fitxers i ara en té tres continua ocupant megabytes: els forats existeixen però el fitxer no encongeix.file_typeduplica informació que ja és a l'inode. És una optimització deliberada: permet afind -type ffiltrar sense llegir l'inode de cada entrada, i estalvia un accés per fitxer.
I el detall que més conseqüències té, ja visible a dalt: el mateix número d'inode pot aparèixer en diverses entrades. Això són els enllaços durs de l'apartat 7.
Evolució de les organitzacions de directoris
L'arbre d'UNIX no va caure del cel. És el quart intent de resoldre el mateix problema, i conèixer els tres anteriors explica per què l'actual és com és.
Nivell únic. Tots els fitxers en un sol espai de noms pla; és el que tenien els primers sistemes per lots i el que tenen encara molts dispositius encastats. Trivial d'implementar, però amb un problema fatal: les col·lisions de noms. Si dos usuaris volen un dades.dat, un hi perd.
Dos nivells. Un directori per usuari penjant d'un directori mestre. Resol les col·lisions i aporta aïllament, però és rígid: un usuari no pot organitzar els seus 400 fitxers en subgrups, i compartir amb un altre és incòmode. Meteora no podria separar lectures/ d'arxiu/.
Arbre jeràrquic. Un directori pot contenir directoris, sense límit de profunditat. És l'estructura d'UNIX i de Windows: organització natural per temes, noms únics garantits pel camí complet, i exactament un camí des de l'arrel fins a cada fitxer. La seva limitació és que no permet compartir de debò: si 2026-08-31.dat també ha d'aparèixer a /srv/publicacio/, l'única opció és copiar-lo —34 MB en lloc de 17, i divergència tan bon punt un dels dos canviï—.
Graf acíclic. L'arbre més la possibilitat que diversos noms apuntin al mateix fitxer, mentre no es formin cicles. És el que UNIX aconsegueix amb els enllaços. Aporta compartició real, però porta dos problemes nous: el recorregut pot visitar un fitxer dues vegades (find, du i tar porten el compte dels inodes ja vistos), i l'esborrat deixa de ser obvi —si dos camins són el mateix inode, esborrar-ne un destrueix el contingut?—. La solució és el comptador d'enllaços de l'apartat 7.
Graf general. Si a més es permeten cicles —un directori que es conté a si mateix per un camí indirecte—, apareixen dos problemes greus:
- Recorreguts infinits. Un
findsense protecció entraria en un bucle etern. Caldria detectar cicles a cada recorregut, amb cost i memòria. - Recol·lecció d'escombraries. El comptador d'enllaços deixa de servir. Si
A/conté un enllaç aB/iB/conté un enllaç aA/, i esborrem tots dos noms des de fora, els dos comptadors continuen valent 1 —se sostenen mútuament— però ningú no hi pot arribar. Aquell espai quedaria perdut per sempre llevat que s'executés un recol·lector d'escombraries per accessibilitat, recorrent el disc sencer des de l'arrel.
I per això UNIX pren una decisió taxativa que ara entens:
No es permeten enllaços durs a directoris. Només
.i.., que crea el mateix nucli i són cicles controlats i coneguts.
Amb aquesta única prohibició, l'estructura de directoris queda garantida com a acíclica, i el comptador d'enllaços n'hi ha prou com a recol·lector d'escombraries: mai no pot haver-hi una illa inabastable amb comptador positiu. És una restricció petita a canvi d'eliminar dues classes senceres de problemes.
| Organització | Col·lisions | Compartició | Cicles | Esborrat segur | Feta servir avui |
|---|---|---|---|---|---|
| Nivell únic | Sí | No | No | Trivial | Encastats simples |
| Dos nivells | No | Difícil | No | Trivial | Sistemes històrics |
| Arbre | No | No | No | Trivial | FAT (sense enllaços) |
| Graf acíclic | No | Sí | No | Comptador d'enllaços | UNIX, Linux, NTFS |
| Graf general | No | Sí | Sí | Necessita recol·lector | Cap de seriós |
Camins absoluts i relatius, . i ..
Un camí és la recepta per arribar a un fitxer recorrent el graf.
- Absolut: comença per
/i parteix de l'arrel./var/lib/meteora/lectures/2026-08-31.dat. Significa el mateix l'executi qui l'executi i des d'on l'executi. - Relatiu: no comença per
/i parteix del directori de treball actual del procés.lectures/2026-08-31.datsignifica coses diferents segons on siguis.
Tot directori conté sempre dues entrades que crea el nucli en crear-lo:
| Entrada | Apunta a | Per a què serveix |
|---|---|---|
. |
El seu propi inode | Referir-se al directori actual: ./programa, cp fitxer . |
.. |
L'inode del seu pare | Pujar un nivell: cd .., ../config/ |
A l'arrel hi ha una excepció elegant: /.. apunta a /. L'arrel és el seu propi pare, així que cd /../../../.. et deixa a / sense error. És el que impedeix sortir de l'arbre per dalt.
Aquestes dues entrades expliquen el comptador d'enllaços dels directoris, que confon molta gent:
Per què 3? Perquè tres noms apunten a aquest inode:
lectures, l'entrada al directori pare/var/lib/meteora/.., dins del mateixlectures/..., dins del seu únic subdirectori,arxiu/.
D'aquí la regla: el comptador d'enllaços d'un directori és 2 + el nombre de subdirectoris. Un truc pràctic que en surt: per saber quants subdirectoris té un directori sense recórrer-lo, n'hi ha prou amb stat -c %h i restar 2. Amb un directori de 100.000 entrades, això és instantani enfront dels segons que trigaria ls -l | grep ^d.
El directori de treball: chdir, getcwd i per què és per procés
Cada procés té el seu directori de treball actual (current working directory, cwd), desat al seu task_struct —l'estructura que vam disseccionar a Gestió de Processos—. És un punter a un inode de directori, no una cadena de text.
Es veu a /proc, com tota la resta:
$ ls -l /proc/2841/cwd lrwxrwxrwx 1 meteora meteora 0 set 1 12:44 /proc/2841/cwd -> /var/lib/meteora
I es manipula amb dues crides al sistema:
#include <unistd.h>
char buf[PATH_MAX];
if (chdir("/var/lib/meteora/lectures") == -1) /* canvia el cwd del procés */
perror("chdir");
if (getcwd(buf, sizeof buf) == NULL) /* reconstrueix el camí actual */
perror("getcwd");
printf("Treballant a %s\n", buf); /* → /var/lib/meteora/lectures */chdir() canvia el punter d'inode del procés —requereix permís d'execució a la destinació, com veurem a Seguretat i Permisos— i getcwd() fa el contrari: parteix de l'inode del cwd i puja per .. fins a l'arrel, buscant a cada pare el nom que correspon a l'inode del fill, i compon la cadena a l'inrevés. Per això getcwd() és sorprenentment cara i per això pot fallar amb ENOENT si algú ha esborrat un directori del camí mentre hi eres a dins.
Per què el cwd és per procés i no global mereix resposta explícita. Si fos global, dos processos que fessin cd es trepitjarien: meteo-api canviaria de directori i l'agregador escriuria al lloc equivocat, una condició de cursa de manual (03-01) sobre estat compartit. En ser per procés, s'hereta al fork() —el fill comença on era el pare, i per això cd /tmp && ls funciona com esperes— i s'explica el desconcert clàssic: un script que fa cd /var/lib/meteora no canvia el directori del teu intèrpret d'ordres, perquè corre en un procés fill el cwd del qual mor amb ell. Perquè afecti l'intèrpret cal executar-lo amb source o ..
Un detall pràctic important per a serveis: un dimoni que manté el seu cwd en un directori impedeix desmuntar aquell sistema de fitxers (el «target is busy» de 04-03). Per això els dimonis ben escrits fan chdir("/") en arrencar, i per això systemd ofereix WorkingDirectory=.
Resolució de camins pas a pas, amb el cost comptat
Aquesta és l'operació central de la lliçó. Quan meteo-api executa:
el nucli ha de convertir 43 caràcters en l'inode 1180934. L'algorisme, anomenat resolució de camins (path resolution), és un bucle:
graph TB
A["Camí: /var/lib/meteora/lectures/2026-08-31.dat"] --> B{"Comença per /?"}
B -->|Sí| C["inode actual = inode de l'arrel (núm. 2)"]
B -->|No| D["inode actual = inode del cwd del procés"]
C --> E["Prendre el component següent"]
D --> E
E --> F{"És un directori?<br/>Hi ha permís x?"}
F -->|No| G["Error: ENOTDIR o EACCES"]
F -->|Sí| H["Buscar el nom a les seves entrades"]
H --> I{"Trobat?"}
I -->|No| J["Error: ENOENT"]
I -->|Sí| K["inode actual = l'inode trobat"]
K --> L{"És enllaç simbòlic?"}
L -->|Sí| M["Substituir per la seva destinació<br/>i reiniciar (màx. 40 vegades)"]
M --> E
L -->|No| N{"Queden components?"}
N -->|Sí| E
N -->|No| O["Retornar l'inode final"]
Executat sobre el nostre camí, amb el compte d'accessos suposant memòries cau buides:
| Pas | Component | Què fa el nucli | Accessos a disc |
|---|---|---|---|
| 0 | / |
Agafa l'inode 2 (l'arrel sempre és l'inode 2 a ext4) | 1 (inode) |
| 1 | var |
Llegeix els blocs de dades de /, busca var → inode 131073 |
1 (dades) + 1 (inode) |
| 2 | lib |
Llegeix les dades de /var, busca lib → inode 262145 |
1 + 1 |
| 3 | meteora |
Llegeix les dades de /var/lib, busca meteora → inode 1180928 |
1 + 1 |
| 4 | lectures |
Llegeix les dades de /var/lib/meteora → inode 1180929 |
1 + 1 |
| 5 | 2026-08-31.dat |
Llegeix les dades de lectures, busca el nom → inode 1180934 |
1 + 1 |
| 6 | — | Llegeix l'inode final per comprovar permisos i tipus | (ja comptat) |
| Total | 11 accessos |
Onze accessos a disc per obrir un fitxer. En un NVMe, amb 80 µs cadascun, són uns 0,9 ms; a l'HDD de 02-05, amb 8 ms de latència mitjana, serien 88 mil·lisegons. I tot això abans de llegir ni un sol byte de dades.
Tres conseqüències directes que val la pena interioritzar:
- Cada component del camí costa.
/a/b/c/d/e/f/g/fitxer.datés més car d'obrir que/dades/fitxer.dat. Camins absurdament profunds tenen un cost real, encara que petit. - El permís d'execució es comprova a cada directori del camí, no només al fitxer final. És el tema de 04-06, però el mecanisme és aquest bucle.
- Obrir una vegada i reutilitzar el descriptor és molt més barat que obrir el fitxer a cada operació. És la raó per la qual l'
ingestorobre2026-08-31.daten començar el dia i el manté obert, en lloc d'obrir-lo i tancar-lo a cada lectura rebuda.
Si algun component intermedi és un enllaç simbòlic, se substitueix per la seva destinació i el bucle es reinicia. Linux limita a 40 les resolucions encadenades; en superar-ho retorna ELOOP («Massa nivells d'enllaços simbòlics»), que és la protecció contra un enllaç que apunta a si mateix.
La memòria cau de dentries: per què això no costa el que sembla
Onze accessos per open() seria inacceptable: un servidor obre milers de fitxers per segon i la majoria comparteixen els primers components del camí. La solució del nucli és la memòria cau de dentries (dentry cache o dcache).
Una dentry (directory entry) és una estructura en memòria que associa (inode del directori pare, nom) → inode fill. La dcache és una taula hash de dentries, amb el camí parcial com a clau. El seu efecte és demolidor: si /var/lib/meteora/lectures és a la memòria cau —i hi serà, perquè es fa servir constantment—, la resolució del nostre camí passa d'11 accessos a disc a un, el de l'inode final, i sovint a zero si també està desat a la memòria cau.
$ sudo slabtop -o | grep -E 'dentry|inode_cache' OBJS ACTIVE USE OBJ SIZE SLABS OBJ/SLAB CACHE SIZE NAME 418320 411927 98% 0,19K 19920 21 79680K dentry 96432 94215 97% 0,58K 3444 28 55104K ext4_inode_cache
En aquest meteo-01 hi ha 418.320 dentries a la memòria cau que ocupen 78 MiB, amb un 98 % d'actives. Aquest consum de RAM és el que fa que el sistema de fitxers sembli ràpid, i és també part del que free compta com a memòria «disponible»: són memòries cau reclamables, exactament com les pàgines de 02-04.
Dos detalls que es veuen a la pràctica:
Dentries negatives. La memòria cau guarda també els «no existeix»: si un intèrpret busca un mòdul per una llista de vint camins, la primera consulta de cadascun costa accessos a disc i les següents retornen ENOENT des de memòria.
Es pot buidar per mesurar. Per comparar el cost real amb i sense memòria cau:
sync # abocar escriptures pendents
echo 3 | sudo tee /proc/sys/vm/drop_caches # 3 = dentries + inodes + pàgines
time cat /var/lib/meteora/lectures/2026-08-31.dat > /dev/null # fred
time cat /var/lib/meteora/lectures/2026-08-31.dat > /dev/null # calentEls valors de drop_caches són 1 (memòria cau de pàgines), 2 (dentries i inodes) i 3 (totes dues). Només en proves: en producció buidar les memòries cau provoca un pic de latència mentre tot es torna a llegir. La diferència entre l'execució freda i la calenta sol ser d'un a dos ordres de magnitud, i és la mesura directa del valor de la dcache.
Enllaços durs: un inode, diversos noms
Un enllaç dur és simplement una altra entrada de directori que apunta al mateix número d'inode. No és una còpia ni una referència especial: és un nom més, amb exactament la mateixa categoria que l'original.
$ cd /var/lib/meteora/lectures $ ls -li 2026-08-31.dat 1180934 -rw-r----- 1 meteora meteora 17280000 ago 31 23:59 2026-08-31.dat $ ln 2026-08-31.dat /var/lib/meteora/arxiu/agost-final.dat $ ls -li 2026-08-31.dat /var/lib/meteora/arxiu/agost-final.dat 1180934 -rw-r----- 2 meteora meteora 17280000 ago 31 23:59 2026-08-31.dat 1180934 -rw-r----- 2 meteora meteora 17280000 ago 31 23:59 .../agost-final.dat
Fixa't en les dues coses que han canviat: el mateix inode 1180934 als dos, i el comptador d'enllaços ha passat d'1 a 2. I el que no ha canviat: l'espai al disc.
Els 17 MB no s'han duplicat perquè no hi ha dos fitxers: hi ha un fitxer amb dos noms. Si escrius per un, ho veus per l'altre, perquè és el mateix inode, els mateixos blocs i els mateixos bytes.
Ara, esborrar-ne un:
$ rm 2026-08-31.dat $ ls -li /var/lib/meteora/arxiu/agost-final.dat 1180934 -rw-r----- 1 meteora meteora 17280000 ago 31 23:59 agost-final.dat
El fitxer continua allà, amb el comptador tornat a 1. rm no destrueix res: treu un nom i decrementa el comptador. Només quan el comptador arriba a 0 el sistema allibera els blocs.
Les dues restriccions dels enllaços durs, amb el seu perquè:
No poden creuar sistemes de fitxers. Un enllaç dur és un número d'inode, i els números d'inode són únics només dins del seu sistema de fitxers (04-01). L'inode 1180934 existeix a /dev/md0 i probablement també a /dev/sda1, sent coses diferents. Una entrada de directori no té espai per dir «de quin dispositiu»:
$ ln /var/lib/meteora/lectures/2026-09-01.dat /tmp/prova.dat ln: no es pot crear l'enllaç dur '/tmp/prova.dat' => ...: Enllaç creuat no vàlid
L'error EXDEV («Invalid cross-device link») és el mateix que fa que mv entre discos diferents hagi de copiar i esborrar en lloc de reanomenar, amb la diferència de rendiment que això suposa: moure 17 GB dins del mateix sistema de fitxers és instantani; entre dos, triga minuts.
No poden enllaçar directoris. És la prohibició de l'apartat 2: garanteix que el graf sigui acíclic i que el comptador d'enllaços n'hi hagi prou com a recol·lector d'escombraries.
$ sudo ln /var/lib/meteora/lectures /var/lib/meteora/drecera ln: /var/lib/meteora/lectures: Operació no permesa
Enllaços simbòlics: un fitxer que conté un camí
Un enllaç simbòlic és un fitxer de tipus l el contingut del qual és una cadena de text amb un camí. Res més. Quan el nucli el troba durant la resolució, substitueix l'enllaç per aquella cadena i continua.
$ ln -s 2026-09-01.dat avui.dat $ ls -li avui.dat 1180936 lrwxrwxrwx 1 meteora meteora 14 set 1 00:00 avui.dat -> 2026-09-01.dat
Tres observacions carregades d'informació: té inode propi (1180936), per tant és un fitxer diferent; la seva mida és 14, exactament els caràcters de 2026-09-01.dat, que són tot el seu contingut; i els seus permisos lrwxrwxrwx són sempre 777 i no signifiquen res, perquè els que s'apliquen són els de la destinació.
Un detall d'implementació bonic: ext4 aprofita els 60 bytes que l'inode reserva per a punters de blocs i, si el camí ocupa menys de 60 caràcters, l'hi guarda allà mateix. Se'n diu fast symlink i no consumeix cap bloc de dades: resoldre'l no costa ni un accés extra. Gairebé tots els enllaços simbòlics reals hi caben.
La diferència crucial amb l'enllaç dur és que el simbòlic apunta a un nom, no a un inode. D'aquí les seves dues propietats característiques:
Pot creuar sistemes de fitxers i enllaçar directoris, perquè una cadena de text no té aquestes limitacions:
Pot quedar-se trencat. Si la destinació desapareix o es reanomena, l'enllaç continua existint i apuntant a un camí que ja no resol:
$ rm 2026-09-01.dat $ ls -l avui.dat lrwxrwxrwx 1 meteora meteora 14 set 1 00:00 avui.dat -> 2026-09-01.dat $ cat avui.dat cat: avui.dat: No existeix el fitxer o el directori
L'enllaç està perfectament sa; el que falta és la destinació. Els enllaços trencats es localitzen amb find /var/lib/meteora -xtype l.
I un parany que mossega tothom: els enllaços relatius es resolen respecte al directori de l'enllaç, no respecte a qui el fa servir.
$ ln -s 2026-09-01.dat /tmp/avui.dat # MALAMENT! $ cat /tmp/avui.dat cat: /tmp/avui.dat: No existeix el fitxer o el directori
/tmp/avui.dat apunta a 2026-09-01.dat, que el nucli busca a /tmp/. Regla pràctica: fes servir camins absoluts als enllaços llevat que enllaç i destinació siguin al mateix directori o vulguis que el conjunt es pugui moure.
La taula comparativa completa:
| Aspecte | Enllaç dur | Enllaç simbòlic |
|---|---|---|
| Què és | Una altra entrada amb el mateix inode | Fitxer amb un camí a dins |
| Inode | El mateix que l'original | Propi i diferent |
Tipus a ls -l |
- (indistingible) |
l |
| Comptador d'enllaços de l'original | Augmenta | No canvia |
| Creua sistemes de fitxers | No (EXDEV) |
Sí |
| Enllaça directoris | No | Sí |
| Sobreviu a l'esborrat de l'original | Sí (el contingut continua) | No (queda trencat) |
| Sobreviu a reanomenar l'original | Sí | No |
| Espai ocupat | 24 bytes al directori | Un inode (+0 blocs si < 60 B) |
| Cost en resoldre | Cap | Una resolució extra |
| Quin és l'«original»? | Cap: són idèntics | La destinació, clarament |
| Ordre | ln origen nou |
ln -s origen nou |
| Ús típic | Còpies de seguretat incrementals, deduplicació | Versions (avui.dat), /usr/bin, biblioteques |
La fila de «quin és l'original?» és la que més costa acceptar: després de ln a b, no hi ha manera de saber quin es va crear primer. Les dues entrades són igual de legítimes; l'inode no guarda cap preferència.
Meteora fa servir tots dos, i cadascun on toca: un simbòlic avui.dat que es reapunta cada mitjanit al fitxer del dia (els clients sempre demanen avui.dat i l'enllaç es refà amb ln -sfn, que és atòmic), i enllaços durs a les còpies de seguretat diàries, on els fitxers que no han canviat s'enllacen en lloc de copiar-se —és exactament el que fa rsync --link-dest i per això trenta còpies diàries de 17 GB poden ocupar 18 GB en total.
Organització interna i el cost dels directoris enormes
Fins ara hem dit que un directori «conté una taula». Com està organitzada aquesta taula? És una decisió d'estructures de dades amb efectes mesurables.
Llista lineal. Les entrades, una darrere l'altra, en l'ordre en què es van crear. Buscar un nom significa recórrer des del principi comparant cadenes.
- Buscar: O(n). Crear: O(n), perquè cal comprovar que el nom no existeix.
- És el que feia ext2 i el que continua fent ext4 en directoris petits.
Fem el càlcul amb un cas realista. Suposa que Meteora, en lloc d'un fitxer per dia, hagués guardat un fitxer per cada deu minuts durant dos anys: 105.120 fitxers a lectures/. Amb entrades de 24 bytes de mitjana:
- Mida del directori: 105.120 × 24 ≈ 2,5 MB, és a dir 616 blocs de 4 KiB.
- Buscar un nom: llegir una mitjana de 308 blocs i comparar 52.560 cadenes.
- I el pitjor: crear un fitxer nou obliga a recórrer els 616 blocs sencers per verificar que el nom no existeix.
El resultat és la patologia clàssica: ls triga segons, i crear els fitxers es torna cada vegada més lent a mesura que el directori creix. És un comportament quadràtic en conjunt: crear n fitxers costa O(n²).
Taula hash. S'aplica una funció hash al nom i se salta directament al calaix que li correspon.
- Buscar: O(1) en el cas mitjà. És el que fan servir ext4 (
dir_index) i NTFS amb variants. - Inconvenient: el recorregut complet retorna les entrades en ordre de hash, que sembla aleatori.
Arbre B / B+. Les entrades es mantenen ordenades en un arbre equilibrat.
- Buscar: O(log n), i a més el recorregut surt ordenat i els rangs són eficients.
- És el que fan servir XFS, Btrfs i NTFS.
A ext4, la solució concreta es diu htree (hashed tree) i s'activa amb la característica dir_index. És un híbrid: aplica una variant d'MD4 al nom i fa servir el hash com a clau d'un arbre d'un o dos nivells, cadascun amb 4 KiB d'índex. Amb dos nivells adreça de l'ordre de milions d'entrades amb com a màxim tres accessos: node arrel, node fulla i bloc de dades.
La comparació, sobre el directori hipotètic de 105.120 fitxers:
| Organització | Buscar un nom | Crear un fitxer | Llistar-ho tot | Ordre de sortida |
|---|---|---|---|---|
| Llista lineal | 308 blocs (mitjana) | 616 blocs | 616 blocs | Creació |
| htree (ext4) | 3 blocs | 3 blocs | 616 blocs | Hash (atzar aparent) |
| Arbre B+ (XFS) | ~3 blocs | ~3 blocs | 616 blocs | Alfabètic |
De 308 a 3 accessos: cent vegades menys. Es comprova si està actiu amb:
$ sudo tune2fs -l /dev/md0 | grep features Filesystem features: has_journal ext_attr dir_index extent 64bit metadata_csum
dir_index hi és, i en qualsevol ext4 creat els últims quinze anys hi serà. Si per la raó que sigui no hi fos, s'activa amb tune2fs -O dir_index /dev/md0 seguit d'e2fsck -D per reconstruir els índexs.
Dues conseqüències pràctiques de l'htree que convé conèixer. La primera és que ls surt desordenat i ordenar-lo costa: el recorregut retorna les entrades en ordre de hash i ls les ordena en memòria abans d'imprimir res, així que amb 105.120 entrades ls -f o find . -maxdepth 1 són molt més ràpids. La segona és que llistar en ordre de hash destrossa la localitat dels inodes: processar-los en l'ordre que retorna readdir() significa llegir-los en ordre aleatori, cosa que en un HDD són cerques contínues. La solució clàssica —i el que fan tar i diverses eines de còpia de seguretat— és llegir tots els noms, ordenar-los per número d'inode i processar-los així.
I la conclusió de disseny, que és la que importa: fins i tot amb htree, un directori amb cent mil fitxers és una mala idea. El llistat continua sent O(n), les còpies de seguretat s'alenteixen, ls amb * pot desbordar la línia d'ordres, i qualsevol eina que ordeni consumeix memòria. La pràctica correcta és repartir en subdirectoris, que és exactament el que fa Meteora amb el seu arxiu històric:
/var/lib/meteora/lectures/2026-08-31.dat ← el dia en curs i els recents /var/lib/meteora/arxiu/2026/08/2026-08-01.dat ← històric per any/mes
Amb aquesta jerarquia, cap directori no passa de 31 entrades. És el mateix patró que fan servir Git per als seus objectes (ab/cdef...) i les memòries cau web de tothom.
L'estàndard FHS i per què Meteora és on és
El Filesystem Hierarchy Standard (FHS) és la convenció que diu què va a cada directori de l'arrel a Linux. No l'imposa el nucli: és un acord que fa que un administrador sàpiga on mirar en qualsevol distribució.
| Directori | Què conté | Persistent? | Compartible? |
|---|---|---|---|
/bin, /sbin |
Binaris essencials del sistema (avui, enllaços a /usr) |
Sí | Sí |
/boot |
Nucli, initramfs i carregador d'arrencada | Sí | No |
/dev |
Fitxers de dispositiu (devtmpfs, 04-03) | No | No |
/etc |
Configuració del sistema, específica d'aquesta màquina | Sí | No |
/home |
Directoris personals dels usuaris | Sí | Sí |
/lib |
Biblioteques compartides essencials | Sí | Sí |
/mnt, /media |
Punts de muntatge temporals i de mitjans extraïbles | — | — |
/opt |
Programari de tercers autocontingut | Sí | Sí |
/proc |
Informació de processos i del nucli (procfs, 04-03) | No | No |
/root |
Directori personal del superusuari | Sí | No |
/run |
Estat en execució: PID, sòcols, FIFO (tmpfs) | No | No |
/srv |
Dades servides per aquest sistema (web, ftp) | Sí | Sí |
/sys |
Interfície amb el model de dispositius (sysfs, 04-03) | No | No |
/tmp |
Temporals de qualsevol usuari, es buida en arrencar | No | No |
/usr |
Programes i dades de només lectura, no essencials | Sí | Sí |
/var |
Dades variables: registres, cues, memòries cau, bases de dades | Sí | No |
La lògica de fons són dos eixos: variable enfront d'estàtic (/var canvia constantment, /usr només en actualitzar) i compartible enfront d'específic (/usr es podria muntar per xarxa i compartir-se entre màquines, /etc no).
Amb això, els camins de Meteora deixen de ser arbitraris:
| Camí de Meteora | Per què allà |
|---|---|
/var/lib/meteora/lectures/ |
/var/lib és el lloc canònic de les dades d'estat d'una aplicació: informació que l'aplicació crea, modifica i necessita conservar entre reinicis. No és /srv perquè no és contingut servit tal qual, ni /opt perquè això és per al programari, no per a les seves dades |
/etc/meteora/meteora.conf |
/etc és configuració específica d'aquesta màquina, editable per l'administrador i que s'ha de conservar a les còpies de seguretat i controlar per versions |
/var/log/meteora/meteo-api.log |
/var/log és la destinació estàndard dels registres, amb rotació gestionada per logrotate |
/run/meteora/lectures.fifo |
/run és tmpfs: es buida en arrencar. Perfecte per a una FIFO i un sòcol, que no han de sobreviure a un reinici: un sòcol orfe d'una execució anterior impediria arrencar el servei |
/dev/shm/meteora-cache |
tmpfs per a memòria compartida POSIX (03-03). Volàtil per definició: és una memòria cau |
/tmp/ |
Només per a temporals de vida curta, amb mkstemp (04-04). Mai per a dades que hagin de durar |
La raó per la qual /run i /dev/shm són tmpfs, i /var/lib no, resumeix el capítol sencer: la ubicació d'un fitxer declara les seves garanties de persistència. Posar el sòcol a /var/run (que avui és un enllaç simbòlic a /run) o les lectures a /tmp no són qüestions de gust: canvien el que passa després d'un reinici.
Esborrat: què fa realment unlink
Arribem al comportament que més desconcerta, i que ara té una explicació completa. La crida al sistema que hi ha darrere de rm no es diu delete:
Els seus passos exactes:
- Resoldre el camí fins al directori pare i comprovar el permís d'escriptura al directori (no al fitxer: 04-06).
- Eliminar l'entrada (nom, inode) del directori, absorbint el seu
rec_lena l'anterior. - Decrementar el comptador d'enllaços de l'inode.
- Si el comptador arriba a 0 i cap procés no té el fitxer obert: marcar l'inode com a lliure al seu mapa de bits i alliberar tots els seus blocs.
- Si el comptador arriba a 0 però algun procés el té obert: no alliberar res. L'inode passa a una llista d'orfes i s'alliberarà quan es tanqui l'últim descriptor.
El pas 5 és el que produeix l'incident clàssic. Algú veu que /var/log/meteora/meteo-api.log ha crescut fins a 17 GB, l'esborra amb rm, i l'espai no apareix:
$ ls -l /var/log/meteora/ total 0 ← el fitxer ja no hi és $ df -h /var/log Sist. fitxers Mida Usat Disp Ús% Muntat a /dev/sda3 20G 19G 340M 99% /var/log ← encara està ple!
El fitxer ja no té nom, però meteo-api el té obert. El comptador d'enllaços val 0, però el comptador de descriptors oberts val 1, així que els blocs continuen reservats i —pitjor encara— el procés continua escrivint en un fitxer que ja ningú no pot obrir.
El diagnòstic:
$ sudo lsof +L1 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME meteo-api 2841 meteora 5w REG 8,3 18253611008 0 4457 /var/log/meteora/meteo-api.log (deleted)
+L1 filtra els fitxers oberts amb menys d'un enllaç, és a dir, els esborrats. La columna NLINK val 0 i el nom porta (deleted). Aquí hi ha el culpable.
Les tres solucions, de millor a pitjor:
# 1. Recarregar el servei: tanca i reobre els seus fitxers (03-03, SIGHUP)
sudo systemctl reload meteo-api # o kill -HUP 2841
# 2. Si no admet recàrrega, reiniciar-lo
sudo systemctl restart meteo-api
# 3. Buidar el fitxer SENSE esborrar-lo (a través de /proc, sense reiniciar res)
sudo truncate -s 0 /proc/2841/fd/5La tercera és un truc excel·lent: /proc/<pid>/fd/5 és un enllaç a l'inode orfe, així que s'hi pot accedir encara que no tingui nom, i truncar-lo a zero allibera els blocs a l'instant amb el servei en marxa. És el mateix /proc/<pid>/fd que explorarem a Gestió de Fitxers.
I la lliçó de fons: la manera correcta de buidar un registre és truncate -s 0 o : > fitxer, mai rm. És exactament el que fa logrotate amb l'opció copytruncate.
Un corol·lari feliç del mateix mecanisme: esborrar un fitxer obert és una tècnica legítima i molt utilitzada. Un programa pot crear un temporal, obrir-lo, esborrar-lo immediatament i continuar fent-lo servir pel descriptor. El fitxer no té nom, així que ningú més no el pot tocar, i el sistema l'allibera tot sol quan el procés mor, fins i tot si mor d'un kill -9. Ho veurem amb mkstemp a 04-04.
Errors Habituals i Consells
Creure que rm esborra el fitxer. Esborra un nom. Si hi ha un altre enllaç dur, el contingut continua; si un procés el té obert, l'espai continua ocupat. rm és unlink, i el nom de la crida és literal.
Buscar espai lliure amb du quan df diu que està ple. du recorre noms, així que no veu els fitxers esborrats que continuen oberts. Si df i du discrepen en gigabytes, executa lsof +L1.
Fer servir camins relatius en enllaços simbòlics sense pensar-hi. ln -s dades.dat /altre/lloc/enllac crea un enllaç que busca dades.dat a /altre/lloc/. Fes servir camins absoluts llevat que sàpigues exactament el que fas.
Confondre el comptador d'enllaços d'un directori amb «quants fitxers té». És 2 + nombre de subdirectoris; els fitxers regulars no compten.
Ficar cent mil fitxers en un directori. Encara que dir_index salvi la cerca, el llistat, l'ordenació, les còpies de seguretat i els comodins de l'intèrpret continuen sent O(n) o pitjor. I un directori no encongeix en esborrar: un que va arribar a 2,5 MB continuarà trigant el mateix a llistar-se encara que quedi buit, i l'única cura és recrear-lo. Reparteix en subdirectoris des del principi: migrar-ho després és dolorós.
Deixar el cwd d'un dimoni dins d'un volum que vols desmuntar. És una causa habitual de «target is busy» (04-03). Els serveis haurien de fer chdir("/").
Consell: fes servir ls -f o find -maxdepth 1 en directoris grans, que eviten l'ordenació en memòria, i stat -c '%i %h %n' per veure inode, comptador d'enllaços i nom d'un cop d'ull.
Consell: quan dubtis de si dos camins són el mateix fitxer, compara st_dev i st_ino, amb stat -c '%d %i' o directament find / -samefile cami. Comparar continguts és lent i comparar noms no diu res.
Exercicis
Exercici 1: demostrar que el nom no és a l'inode
Crea un fitxer, fes-li un enllaç dur i un de simbòlic, i dissenya una seqüència d'ordres que demostri les cinc afirmacions següents, mostrant la sortida que ho prova en cada cas: (a) l'enllaç dur i l'original comparteixen inode i el simbòlic no; (b) crear l'enllaç dur no consumeix espai de dades; (c) esborrar l'original no destrueix el contingut si hi ha enllaç dur; (d) esborrar l'original deixa trencat l'enllaç simbòlic; (e) el comptador d'enllaços reflecteix exactament el nombre de noms. Explica a cada punt quin camp de l'inode ho justifica.
Exercici 2: el cost de la resolució de camins
Calcula el nombre d'accessos a disc necessari per obrir /var/lib/meteora/arxiu/2026/08/2026-08-15.dat amb les memòries cau buides, detallant els passos. Després, suposant que /var/lib/meteora/arxiu/2026/08/ ja és a la memòria cau de dentries, recalcula-ho. Amb una latència de 80 µs per accés en NVMe i de 8 ms en HDD, dona els quatre temps. Finalment, explica què canvia si arxiu és un enllaç simbòlic a /mnt/historic/meteora i quants accessos hi afegeix.
Exercici 3: el fitxer esborrat que no allibera espai
Reprodueix l'incident complet: escriu un programa (en intèrpret d'ordres o en C) que obri un fitxer, hi escrigui contínuament i no el tanqui; en un altre terminal, esborra el fitxer i comprova que df no baixa. Diagnostica-ho amb lsof i du enfront de df, i resol-ho sense matar el procés. Després explica per què existeix logrotate amb copytruncate, i què hauria passat si en lloc de rm s'hagués fet servir truncate -s 0.
Solucions
Solució 1
cd /tmp && mkdir demo && cd demo
dd if=/dev/urandom of=original.dat bs=1M count=10 status=none
df --output=avail /tmp | tail -1 # espai lliure ABANS: p. ex. 8123456
ln original.dat dur.dat
ln -s original.dat tou.dat
df --output=avail /tmp | tail -1 # espai lliure DESPRÉS: idèntic
ls -liSortida esperada:
264531 -rw-r--r-- 2 joan joan 10485760 set 1 13:02 dur.dat 264531 -rw-r--r-- 2 joan joan 10485760 set 1 13:02 original.dat 264532 lrwxrwxrwx 1 joan joan 12 set 1 13:02 tou.dat -> original.dat
(a) original.dat i dur.dat comparteixen l'inode 264531; tou.dat té el 264532, propi. L'enllaç dur és una entrada de directori més que apunta al mateix inode; el simbòlic és un fitxer diferent el contingut del qual són els 12 caràcters original.dat.
(b) L'espai lliure no ha canviat després d'ln: un enllaç dur afegeix 24 bytes a un bloc del directori ja reservat, sense tocar la zona de dades. El camp de l'inode que ho justifica és que els punters a blocs no s'han duplicat: continuen sent els mateixos.
(c) i (e):
rm original.dat
ls -li dur.dat # → 264531 -rw-r--r-- 1 ... dur.dat
md5sum dur.dat # el contingut íntegre continua allàEl comptador d'enllaços va passar de 2 a 1. unlink va decrementar el comptador; com que no va arribar a 0, els blocs no es van alliberar. El camp és i_nlink.
(d):
ls -l tou.dat # l'enllaç continua existint, amb la seva mida 12
cat tou.dat # cat: tou.dat: No existeix el fitxer o el directori
find . -xtype l # ./tou.datL'enllaç simbòlic guarda el nom original.dat, no l'inode. En desaparèixer aquest nom del directori, la resolució falla amb ENOENT a l'últim component. Curiosament, ln -s dur.dat tou2.dat funcionaria, perquè el contingut continua accessible per aquell altre nom: prova que el simbòlic depèn del nom i el dur de l'inode.
Solució 2
El camí té 6 components: var, lib, meteora, arxiu, 2026, 08, 2026-08-15.dat. En realitat en són 7.
Memòries cau buides. Un accés per a l'inode de l'arrel, i per cada component un per llegir les dades del directori i un altre per llegir l'inode trobat:
1 (inode arrel) + 7 × 2 = 15 accessos.
Amb /var/lib/meteora/arxiu/2026/08/ a la dcache, la resolució arrenca directament des de l'inode de 08/ (que la memòria cau té), i només queda buscar l'últim component: 1 (dades de 08/) + 1 (inode final) = 2 accessos. I si l'inode final també fos a la memòria cau, 0.
| Escenari | Accessos | NVMe (80 µs) | HDD (8 ms) |
|---|---|---|---|
| Memòries cau buides | 15 | 1,2 ms | 120 ms |
| Dentries a la memòria cau | 2 | 0,16 ms | 16 ms |
Els 120 ms de l'HDD fred són la raó que la primera arrencada d'un servei amb molts fitxers sigui tan lenta i la segona gairebé instantània, i una justificació numèrica de la dcache.
Amb arxiu com a enllaç simbòlic a /mnt/historic/meteora: en arribar a arxiu, el nucli llegeix el seu inode, detecta el tipus l, i —si el camí ocupa menys de 60 bytes, com aquí (24)— el llegeix del mateix inode, sense accés extra. Després reinicia la resolució amb el nou camí absolut: arrel, mnt, historic, meteora, i després 2026, 08 i el fitxer.
Recompte en fred: 1 (arrel) + 2×3 (var, lib, meteora) + 2 (arxiu, l'inode del qual ja porta la destinació) + 1 (arrel una altra vegada, segurament a la memòria cau) + 2×3 (mnt, historic, meteora) + 2×3 (2026, 08, fitxer) = ~22 accessos, uns 7 més. Si l'enllaç fes 60 bytes o més, caldria llegir a més el seu bloc de dades: un accés addicional per cada enllaç travessat. És poc, però explica per què les cadenes llargues d'enllaços simbòlics en camins crítics es noten.
Solució 3
Reproducció:
# Terminal 1
( while :; do dd if=/dev/zero bs=1M count=10 status=none; sleep 1; done ) > /tmp/gros.log &
echo $! # anota el PID, p. ex. 5512
# Terminal 2
sleep 30
df -h /tmp | tail -1 # l'ús puja
rm /tmp/gros.log
ls -l /tmp/gros.log # No existeix
df -h /tmp | tail -1 # l'ús NO baixa, i continua pujant!
du -sh /tmp # du mostra molt menys que df: la discrepànciaDiagnòstic:
sudo lsof +L1 /tmp
# COMMAND PID USER FD TYPE DEVICE SIZE/OFF NLINK NODE NAME
# dd 5513 joan 1w REG 0,25 943718400 0 312 /tmp/gros.log (deleted)NLINK 0 i (deleted): l'inode 312 no té cap nom però continua viu perquè el descriptor 1 del procés 5513 el manté obert. unlink va completar els passos 1-3 i es va aturar al 5.
Resolució sense matar el procés:
/proc/<pid>/fd/1 dona accés a l'inode orfe a través del descriptor obert. Truncar-lo a 0 allibera tots els seus blocs a l'instant. El procés continua escrivint —en un fitxer que ara comença de zero—, sense haver-se'n assabentat.
Per què existeix copytruncate. Quan logrotate reanomena un registre i en crea un de nou, un servei que mantingui el descriptor obert continuarà escrivint al fitxer reanomenat, perquè el descriptor apunta a l'inode, no al nom (aquesta és tota la lliçó). Hi ha dues solucions: enviar SIGHUP al servei perquè reobri el fitxer pel seu nom —l'elegant, i el que fa Meteora des de 03-03—, o fer servir copytruncate, que copia el contingut i trunca l'original a zero sense canviar l'inode, de manera que el descriptor del servei continua sent vàlid. És l'opció per a programes que no saben reobrir el seu registre, i té el risc de perdre les línies escrites entre la còpia i el truncament.
Amb truncate -s 0 en lloc de rm no hi hauria hagut incident en cap moment: el nom continua existint, l'inode continua sent el mateix, el descriptor del procés continua sent vàlid, i els blocs s'alliberen immediatament. L'única peculiaritat és que un procés amb O_APPEND continua al final (que ara és 0) mentre que un sense O_APPEND manté el seu desplaçament antic i crea un fitxer dispers (04-04). Per això la regla és: per buidar un registre en ús, truncate -s 0 o : > fitxer, mai rm.
Conclusió
Un directori és un fitxer el contingut del qual és una taula de parells (nom, número d'inode). L'hem obert amb debugfs i hem vist l'estructura real d'ext4: 8 bytes de capçalera amb inode, rec_len, name_len i file_type, més el nom. D'aquest format surten dos comportaments que es veuen cada dia: els directoris no encongeixen en esborrar entrades, i un mateix inode pot aparèixer en diverses entrades, que és la definició d'enllaç dur.
L'organització en graf acíclic d'UNIX és el quart intent històric, i cadascun dels anteriors va caure per un motiu concret: el nivell únic per les col·lisions de noms, els dos nivells per la seva rigidesa, l'arbre pur per no permetre compartir. El graf general es descarta per dos problemes greus —recorreguts infinits i escombraries inabastables que el comptador d'enllaços no detecta—, i per això UNIX prohibeix els enllaços durs a directoris: una sola restricció que garanteix l'aciclicitat i fa del comptador d'enllaços un recol·lector d'escombraries suficient.
La resolució de camins és un bucle component a component que arrenca a l'arrel o al cwd del procés, comprova tipus i permís a cada pas, i expandeix els enllaços simbòlics amb un topall de 40 per evitar ELOOP. Amb memòries cau buides costa 11 accessos a disc per a /var/lib/meteora/lectures/2026-08-31.dat —0,9 ms en NVMe, 88 ms en HDD—, i amb la memòria cau de dentries calenta baixa a un o a cap. Aquells 78 MiB de dentries a meteo-01 són la raó que el sistema de fitxers sembli instantani.
Els dos tipus d'enllaç es distingeixen per una sola cosa, de la qual se'n dedueix tota la resta: el dur apunta a un inode i el simbòlic a un nom. Per això el dur no creua sistemes de fitxers ni enllaça directoris, sobreviu a l'esborrat i al reanomenament, i no té «original»; i per això el simbòlic creua el que vulgui, es trenca amb facilitat i costa una resolució extra —encara que a ext4, si fa menys de 60 caràcters, ni tan sols consumeix un bloc—.
El cost dels directoris enormes és un problema clàssic d'estructures de dades: la llista lineal fa la cerca O(n) i la creació de n fitxers O(n²), mentre que l'htree d'ext4 (dir_index) resol en tres accessos el que costava tres-cents. Però fins i tot així, cent mil fitxers en un directori continuen sent mala idea, i la resposta correcta és repartir en subdirectoris, com fa l'arxiu històric de Meteora a arxiu/2026/08/.
L'FHS explica per què cada camí de Meteora és on és, i la seva lògica es resumeix en una frase que convé memoritzar: la ubicació d'un fitxer declara les seves garanties de persistència. /var/lib per a les dades que han de durar, /etc per a la configuració d'aquesta màquina, /var/log per als registres, i /run i /dev/shm —que són tmpfs— per al que ha de desaparèixer en reiniciar.
I unlink tanca el cercle de l'inode sense nom: treu una entrada de directori, decrementa el comptador d'enllaços, i allibera els blocs només si el comptador arriba a 0 i ningú no el té obert. D'aquí l'incident dels 17 GB que no s'alliberen, que es diagnostica amb lsof +L1 i es cura amb truncate -s 0 /proc/<pid>/fd/N sense reiniciar res.
Ens falta l'última baula. Hem dit «el sistema de fitxers de /var/lib/meteora», «el de /run», «el de /proc», com si l'arbre únic que recorrem amb els camins estigués fet de peces diferents. I ho està: / és un ext4 sobre una partició, /var/lib/meteora és un altre ext4 sobre el RAID /dev/md0, /run és tmpfs a la RAM, /proc no té dispositiu al darrere. Com s'enganxen totes aquestes peces en un arbre continu pel qual la resolució de camins camina sense assabentar-se del canvi? Què passa exactament en executar mount? I com pot el mateix open() funcionar sobre un SSD, sobre la RAM i sobre un servidor a l'altra banda de la xarxa?
És el que veurem a Particions, Muntatge i Sistema de Fitxers Virtual.
Fonaments de Sistemes Operatius
Mòdul 1: Introducció als Sistemes Operatius
- Conceptes Bàsics de Sistemes Operatius
- Història i Evolució dels Sistemes Operatius
- Tipus de Sistemes Operatius
- Funcions Principals d'un Sistema Operatiu
- Arquitectura del Nucli: Monolític, Microkernel i Híbrid
- Mode Usuari, Mode Nucli i Crides al Sistema
Mòdul 2: Gestió de Recursos
- Gestió de Processos
- Planificació de la CPU
- Gestió de Memòria
- Memòria Virtual i Paginació
- Gestió d'Emmagatzematge
- Gestió de Dispositius
- Controladors, Interrupcions i Operacions d'E/S
Mòdul 3: Concurrència
- Conceptes de Concurrència
- Fils i Processos
- Comunicació entre Processos (IPC)
- Sincronització i Exclusió Mútua
- Problemes Clàssics de Concurrència
- Interbloquejos: Prevenció, Detecció i Recuperació
Mòdul 4: Estructures de Fitxers
- Sistemes de Fitxers
- Estructures de Directoris
- Particions, Muntatge i Sistema de Fitxers Virtual
- Gestió de Fitxers
- Assignació d'Espai, Journaling i Integritat
- Seguretat i Permisos de Fitxers
Mòdul 5: Protecció i Seguretat del Sistema
- Principis de Protecció i Control d'Accés
- Usuaris, Autenticació i Escalada de Privilegis
- Amenaces Habituals i Enfortiment del Sistema
- Auditoria, Registres i Resposta a Incidents
Mòdul 6: Virtualització i Contenidors
- Virtualització: Hipervisors i Màquines Virtuals
- Contenidors: Namespaces i cgroups
- El Sistema Operatiu al Núvol
- Sistemes Operatius Mòbils i de Temps Real
