Operační systémy
Obsah Soubory
Soubory a zařízení

Souborový systém

Aktualizováno 10 min čtení 1 874 slov

Souborový systém: i-uzly, adresáře a odkazy

Smažeš rm dvougigabajtový log, df -h ukáže pořád stejně volného místa a nginx, který ho měl otevřený, do něj zapisuje dál. Nic nehlásí chybu.

Nebo obráceně: jeden soubor je vidět ve dvou adresářích naráz, v každém pod jiným jménem. Jeden z nich smažeš a druhý funguje dál i s daty.

Ani jedno není chyba a ani jedno není zvláštní režim. Obojí plyne z jedné věci, kterou většina lidí o souborech neví, protože v běžné práci ji nikdy neuvidí.

Stránka je o tom, jak jsou soubory na disku doopravdy zařízené: co je i-uzel, proč je adresář jen tabulka, jak se z i-uzlu dostaneš k datům a proč se ext4 chová jinak než FAT. Neřeší, co přežije výpadek proudu uprostřed zápisu - to je žurnálování a konzistence - ani jak se souborový systém zapojuje do stromu adresářů, což je VFS a montování. Předpokládá, že víš, co je systémové volání.

Jméno souboru není soubor

Tohle je nejdůležitější věta na celé stránce: soubor je i-uzel, jméno je jen záznam v adresáři, který na něj ukazuje. I-uzel zmizí teprve tehdy, když na něj neukazuje žádné jméno a zároveň žádný otevřený deskriptor.

Zbytek stránky je rozvedení téhle věty. rm se ve skutečnosti jmenuje unlink a dělá přesně to: odpojí jméno. Kolik jmen na i-uzel ukazuje, si počítá sám i-uzel, otevřené deskriptory k tomu připočítává jádro. Nula a nula znamená uvolnit bloky.

Co v i-uzlu je a co v něm není

I-uzel (inode) je záznam pevné velikosti, u ext4 obvykle 256 bajtů. Drží typ souboru a práva, UID vlastníka a GID skupiny, velikost v bajtech, počet pevných odkazů, ukazatele na datové bloky a tři časy. Ty časy si lidi pletou nejčastěji:

  • mtime - kdy se změnil obsah. Tohle chceš skoro vždycky.
  • ctime - kdy se změnil i-uzel: práva, vlastník, počet odkazů. Změna obsahu ho mění taky. Nedá se nastavit dozadu, což je při vyšetřování průšvihu jediné, co ti zbyde.
  • atime - kdy se naposledy četlo. Od jádra 2.6.30 (2009) se montuje s relatime a atime se přepíše, jen když je starší než mtime nebo starší než den. Jinak by každé čtení znamenalo zápis.

Co v i-uzlu není: jméno souboru. Pusť si stat soubor.txt:

  Size: 10          Blocks: 8       IO Block: 4096   regular file
Device: 259,2   Inode: 1837042      Links: 1
Access: (0644/-rw-r--r--)  Uid: ( 1000/  martin)   Gid: ( 1000/  martin)
Modify: 2026-08-06 21:03:55 +0200
Change: 2026-08-07 08:59:11 +0200

Jméno v tom výpisu chybí - stat ho tiskne jen proto, žes mu ho sám napsal na příkazovou řádku. Zajímavá jsou dvě čísla: Inode: 1837042 je identita souboru v rámci oddílu a Blocks: 8 je osm půlkilobajtových jednotek, čili jeden 4KiB blok. Deset bajtů zabírá 4 096.

Adresář je taky jen soubor

Adresář je soubor, jehož obsahem jsou dvojice jméno → číslo i-uzlu. Nic víc v něm není a zapisovat do něj smí jen jádro.

Odtud plyne věc, která vypadá jako díra a není: abys mohl smazat soubor, potřebuješ právo zápisu do adresáře, ne k souboru.

ls -l cizi.txt        # -rw------- 1 root root 42 Aug  7 09:12 cizi.txt
rm cizi.txt           # zeptá se, ale projde - adresář je tvůj

Tohle je nejlepší možný důkaz té ústřední věty. A je to důvod, proč má /tmp sticky bit: zapisovat tam smí každý, takže bez něj by ti kdokoli mohl smazat cokoli. Detaily jsou na stránce práva a uživatelé.

ln a ln -s nejsou dvě varianty téhož

Pevný odkaz je druhé jméno

ln soubor.txt kopie.txt        # nový záznam v adresáři na TENTÝŽ i-uzel
ls -li soubor.txt kopie.txt    # -i vypíše číslo i-uzlu jako první sloupec

Obě jména mají stejné číslo i-uzlu a počet odkazů 2. Není mezi nimi originál a kopie, jsou rovnocenná. Smažeš jedno, počet klesne na jedna a data se nikam nehnou.

Symbolický odkaz je soubor s cestou uvnitř

ln -s vyrobí nový i-uzel vlastního typu, jehož obsahem je textová cesta k cíli. ext4 cestu do 59 bajtů uloží rovnou do i-uzlu, takže symbolický odkaz obvykle nezabere ani blok.

Vlastnost Pevný (ln) Symbolický (ln -s)
Vlastní i-uzel ne, sdílí s cílem ano
Přes hranici oddílu ne ano
Na adresář ne ano
Po smazání cíle funguje dál ukazuje do prázdna
Po přejmenování cíle funguje dál rozbije se
ls -l ukáže obyčejný soubor l a -> cesta

Přes hranici oddílu pevný odkaz neprojde proto, že v adresáři je uložené číslo i-uzlu a to platí jen uvnitř jednoho souborového systému. Když nevíš, ber ln -s - je vidět a dá se opravit, kdežto pevný odkaz vypadá jako obyčejný soubor a člověk, který ho najde za rok, netuší, že úpravou jednoho mění i druhý.

Proč pevný odkaz na adresář nejde

Vznikl by cyklus. Adresáře tvoří strom právě proto, že do každého vede jediná cesta shora. Smyčkou by find, du ani rm -r neprošly - nemají jak poznat, že už tam byly - a počítání odkazů by u cyklu nikdy nekleslo na nulu.

Výjimkou jsou . a .., které si zakládá souborový systém sám. Odtud plyne účetnictví, které mate: prázdný adresář má počet odkazů 2 - jednou jeho jméno v rodiči, podruhé jeho vlastní .. Každý podadresář přidá další, protože jeho .. ukazuje zpátky. S počtem souborů uvnitř to nesouvisí.

Jak se i-uzel dostane k datům

Přímé a nepřímé bloky

Klasické unixové schéma má v i-uzlu patnáct ukazatelů: dvanáct přímých na datové bloky, jeden jednoúrovňový nepřímý (ukazuje na blok plný ukazatelů), jeden dvouúrovňový a jeden trojúrovňový. Při bloku 4 KiB a čtyřbajtovém ukazateli se do jednoho bloku vejde 1 024 ukazatelů:

Úroveň Bloků Dat
12 přímých 12 48 KiB
jednoúrovňový 1 024 4 MiB
dvouúrovňový 1 048 576 4 GiB
trojúrovňový 1 073 741 824 4 TiB

Malý soubor je dostupný okamžitě, na konec velkého se prokoušeš až po třech dalších čteních. To je záměr: většina souborů v systému je malá.

Extenty, čili co s tím udělal ext4

Souvislý 1GiB soubor potřebuje ve starém schématu čtvrt milionu ukazatelů, přestože jde o jeden jediný souvislý rozsah.

ext4 (výchozí od roku 2008) to nahradil extenty. Extent je trojice „logický blok, fyzický blok, délka" a pokryje až 32 768 bloků, čili 128 MiB. Do i-uzlu se vejdou čtyři, na víc se rozvine strom. Ten samý 1GiB soubor je pak popsaný osmi extenty. Kromě místa to šetří hlavně čtení metadat a dovoluje alokovat velké kusy naráz.

Kam se bloky ukládají a proč se Linux „nedefragmentuje"

Bloky se dají rozdat třemi způsoby. Souvislá alokace je nejrychlejší na čtení a nepoužitelná, protože soubor nemůže růst. Seznamová dá do každého bloku odkaz na další a tím zabije náhodný přístup - přesně to dělá FAT. Indexová drží seznam bloků stranou v i-uzlu, a to je Unix.

Vnitřní fragmentaci neřeší žádná: blok je nejmenší jednotka, takže soubor o deseti bajtech zabere 4 KiB a 4 086 bajtů se zahodí. Na dvou stech tisících drobných souborech je to skoro gigabajt.

Linux se opravdu nedefragmentuje a není to mýtus. ext4 používá zpožděnou alokaci: data drží v page cache a fyzické bloky vybere až při zápisu na disk, kdy zná celkovou velikost. K tomu přidává víceblokovou alokaci a rezervaci místa za koncem rostoucího souboru. Běžný soubor tak vznikne jako jeden nebo dva extenty a nemá se co rozsekat.

Kdy to přestává platit: nad zhruba devadesát procent zaplnění. Volné místo přestane být souvislé, alokátor bere, co zbylo, a soubory vznikají rozsekané na desítky kusů. Rady na defragmentaci, které v návodech pořád visí, řeší následek jiného problému: správná odpověď je uvolnit místo, ne pouštět e4defrag.

FAT jako protiklad

Na začátku oddílu je tabulka, ve které má každý cluster položku s číslem clusteru následujícího - spojový seznam. Adresářový záznam nese jméno, velikost, atributy a číslo prvního clusteru. Žádný i-uzel neexistuje: identita souboru a jeho jméno jsou jedna a ta samá věc.

Z toho plyne, co FAT nemá: vlastníka, práva, pevné ani symbolické odkazy, žurnál. Systém na něm provozovat nejde už proto, že by nebyl rozdíl mezi rootem a kýmkoli jiným.

Přesto ho používáš denně - USB klíčenka, kterou přečte i patnáct let staré rádio, a EFI oddíl, kde FAT vyžaduje přímo specifikace UEFI, protože firmware musí načíst zavaděč bez ovladačů (viz start systému).

Systém Největší soubor Práva Konzistence Kdy ho chceš
FAT32 4 GiB - 1 B ne žádná EFI oddíl, staré přístroje
exFAT prakticky bez limitu ne žádná velké SD karty sdílené s Windows
ext4 16 TiB ano žurnál výchozí volba na systém i data
btrfs 16 EiB ano copy-on-write snapshoty, kontrolní součty, více disků
XFS 8 EiB ano žurnál velké soubory, hodně paralelních zápisů

Když nevíš, ber ext4. btrfs si vezmi, když opravdu chceš snapshoty nebo detekci tichého poškození dat, XFS na server s videem.

Pět procent, na která nesmíš

ext4 si při vytvoření rezervuje 5 procent bloků pro roota. Když se disk zaplní, systémové služby mají ještě kam zapsat a stroj se dá zachránit, a alokátor navíc drží rezervu, aby při plném disku nezačal hned rozsekávat soubory.

Na kořenovém oddíle to nech být. Na dvouterabajtovém datovém disku je to sto gigabajtů, které nikdy nikdo nepoužije:

tune2fs -l /dev/nvme0n1p2 | grep -i "reserved block"   # kolik je vyhrazeno teď
tune2fs -m 1 /dev/nvme0n1p2                            # sniž na 1 %, jen u datového oddílu

Když si du a df odporují

ls -l čte velikost z i-uzlu, du sčítá skutečně přidělené bloky. Obě čísla bývají jiná a obě jsou správně.

Řídký soubor (sparse) má díry, kterým nikdy nebyly přiděleny bloky. Čtení z díry vrátí nuly, na disku ale není nic:

truncate -s 1G prazdny.img
ls -lh prazdny.img     # 1,0G
du -h prazdny.img      # 0

Takhle fungují obrazy disků virtuálních strojů. Pozor při přenosu: rsync bez -S a tar bez -S díry vyplní nulami a z jednogigabajtového souboru je rázem gigabajt na disku.

Druhý případ je ten z úvodu. Smazaný, ale stále otevřený soubor už nemá jméno, takže ho du při procházení stromu nenajde. Jeho bloky pořád existují, takže je df počítá jako obsazené. Rozdíl mezi du -sh / a df -h / je přesně to, co drží běžící procesy.

lsof +L1               # soubory s počtem odkazů 0, které někdo drží otevřené
: > /proc/1234/fd/7    # uvolní místo bez restartu procesu

Druhý příkaz zkrátí soubor přes deskriptor. Spolehlivě funguje u procesu, který zapisuje v režimu append - jinak bude psát dál na svůj starý offset a vyrobí ti řídký soubor.

Toy Story 2, rok 1998

Během výroby filmu někdo pustil na sdíleném svazku Pixaru rm -rf na špatném místě. Soubory mizely za běhu a animátoři sledovali, jak se ztrácejí celé adresáře s postavami a scénami. Než stačili stroj odpojit, byla pryč většina filmu - podle pozdějších vyjádření technického ředitele zhruba devadesát procent.

Záloha existovala. Jenže zálohovací systém tiše selhával a nikdo si toho nevšiml, protože obnovu nikdy nikdo nezkusil. Film zachránila kopie, kterou měla doma technická ředitelka Galyn Susmanová - pracovala z domova po narození dítěte a data si k sobě synchronizovala.

Poučení: záloha, kterou jsi nikdy neobnovil, není záloha. Je to jen složka, o které si to myslíš.

Diagnostika

df -h /var                 # volné bajty
df -i /var                 # volné i-uzly - jiný zdroj, dochází dřív
lsof +L1                   # smazané a stále otevřené soubory
ls -li                     # čísla i-uzlů a počty odkazů
stat soubor.txt            # všechno o jednom i-uzlu
filefrag -v velky.img      # na kolik extentů je soubor rozsekaný
du -sh --apparent-size .   # velikost proti skutečně obsazenému místu
Příznak Kde je problém
„No space left on device" a df -h ukazuje volno došly i-uzly, ověř df -i
Smazal jsi logy, místo nepřibylo proces drží soubor otevřený, lsof +L1
du -sh / a df -h / si odporují totéž, smazané a otevřené soubory
du hlásí míň než ls -l řídký soubor, díry nezabírají bloky
Soubor nejde smazat, i když je tvůj chybí zápis do adresáře, sticky bit nebo chattr +i
Uživateli došlo místo, rootovi ne 5 procent vyhrazených bloků
Kopírování 5GB souboru na klíčenku selže FAT32, limit 4 GiB - 1 B

Počet i-uzlů se u ext4 stanoví při mkfs a později se nedá zvětšit - to je klasika serverů s poštou nebo cache. Jediné řešení je přeformátovat s mkfs.ext4 -i 8192 a obnovit ze zálohy. Víc nástrojů je na stránce nástroje a diagnostika.

Co si odnést

Soubor je i-uzel, jméno je ukazatel na něj. Všechno divné chování kolem mazání plyne z téhle věty.

Mazání je odebrání jména. Data zmizí, až když nezbyde jméno ani otevřený deskriptor.

Právo mazat je právo zápisu do adresáře. K samotnému souboru nepotřebuješ nic.

Pevný odkaz je rovnocenné jméno, symbolický je soubor s cestou uvnitř. První neprojde přes oddíl, druhý se rozbije přejmenováním cíle.

Extenty popisují rozsahy, ne jednotlivé bloky. Osm položek místo čtvrt milionu ukazatelů.

Zaplnění nad devadesát procent láme alokátor. Do té doby se ext4 fragmentuje zanedbatelně.

Místo a i-uzly jsou dva nezávislé zdroje. Každý dochází zvlášť a df -h o tom druhém mlčí.

Kam dál