-
Notifications
You must be signed in to change notification settings - Fork 0
Souborovy system
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í.
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.
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
relatimea 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 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ůjTohle 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 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í sloupecObě 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.
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ý.
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í.
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á.
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.
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.
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.
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íluls -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 # 0Takhle 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 procesuDruhý 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.
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íš.
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.
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čí.
- Žurnálování a konzistence - co z těchhle struktur přežije výpadek proudu
- VFS a montování - jak se ext4, tmpfs a FAT tváří pro programy stejně
- Práva a uživatelé - rwx bity, setuid a sticky bit, které sedí v i-uzlu
- Vstup a výstup - page cache a proč se zápis nekoná hned
-
Nástroje a diagnostika -
lsof,stracea/procna živém systému
Našel jsi chybu nebo něco chybí? Založ issue nebo pošli pull request.
Základy
- Co dělá operační systém
- Režim jádra a uživatelský režim
- Systémová volání
- Přerušení a výjimky
- Architektury jádra
Procesy
Souběh
Paměť
Soubory a zařízení
Systém v provozu
Praxe