Portál AbcLinuxu, 25. dubna 2024 09:10

Sjednocující souborové systémy: Implementace, část 2.

15. 6. 2009 | Jirka Bourek
Články - Sjednocující souborové systémy: Implementace, část 2.  

V tomto článku se blíže podíváme na dva sjednocující souborové systémy pro Linux: unionfs a aufs.

Originál tohoto článku napsala Valerie Aurora (dříve Henson), vyšel na LWN.net.

V prvním článku tohoto seriálu o sjednocujících souborových systémech jsme prošli terminologii a hlavní problémy návrhu sjednocujících souborových systémů. V druhém článku jsme popsali tři implementace sjednocujících připojení: Plan 9, BSD a v Linuxu.

I když mají sjednocující připojení a sjednocující souborové systémy stejné cíle, pod kapotou se značně liší. Sjednocující připojení jsou záležitostí operačního systému, implementovány přímo uprostřed kódu VFS; obvykle vyžadují pár malých modifikací souborového systému pod nimi. Sjednocující souborové systémy jsou implementovány v prostoru mezi VFS a souborovým systémem ve spojení s pouze málo nebo žádnými změnami mimo kód samotného sjednocujícího souborového systému. Se sjednocujícím souborovým systémem si VFS myslí, že pracuje s běžným souborovým systémem, a souborový systém si myslí, že pracuje s VFS; oba dva ve skutečnosti pracují se sjednocujícím souborovým systémem. Jak uvidíme, oba přístupy mají své výhody a nevýhody.

Unionfs

Unionfs je nejznámější a nejstarší implementace sjednocujícího souborového systému v Linuxu. Vývoj Unionfs začal v SUNY Stony Brook roku 2003 jako součást projektu skládatelného souborového systému FiST. Oba projekty vede Erez Zadok, profesor na Stony Brook a aktivní přispěvatel do linuxového jádra. Do Unionfs během let přispívalo mnoho vývojářů; kompletní seznam bývalých studentů najdete na webové stránce unionfs – popřípadě si můžete přečíst poznámky o autorských právech v kódu unionfs.

Unionfs existuje ve dvou hlavních verzích, 1.x a 2.x. Verze 1 je původní implementace započatá roku 2003. Verze 2 je přepis, jehož cílem je oprava některých problémů verze 1; tato verze je aktivně vyvíjena. Dokument o návrhu verze 2 je k dispozici na http://www.filesystems.org/unionfs-odf.txt. Nejsou implementovány všechny vlastnosti, které tento dokument popisuje (alespoň ne ve veřejně dostupném gitovém stromě); například vybělení se stále ukládají jako položky v adresáři se zvláštním jménem, což znečišťuje jmenný prostor a znemožňuje skládání jednoho unionfs souborového systému nad jiný.

Základní struktura Unionfs

Kód unionfs je vložen mezi VFS a použité souborové systémy (větve). Unionfs se ve VFS registruje jako souborový systém a komunikuje s ním pomocí standardních rozhraní mezi VFS a souborovým systémem. Dodává různé sady operací se souborovým systémem (jako jsou operace se superblokem, které udávají, jak nastavit souborový systém při připojení, alokovat nové inody, synchronizovat změny na disk a zrušit vlastní datové struktury při odpojení). Na úrovni datových struktur mají souborové systémy unionfs svůj vlastní superblok, struktury pro připojení, inode, dentry a soubory, které se připojují k těm od souborových systémů ve spojení. Každý objekt souborového systému unionfs obsahuje pole ukazatelů na relevantní prvky větví níže. Například privátní data záznamu dentry v unionfs (uchovávaný v d_fsdata) vypadají takto:

/* dentry unionfs, data v paměti */
struct unionfs_dentry_info {
        /*
         * Semafor se používá k zamčení dentry, jakmile se z VFS dostaneme  
         * do funkce unionfs. Řazení zamykání je takové, že potomci jdou 
         * před svými rodiči.
         */
        struct mutex lock;
        int bstart;
        int bend;
        int bopaque;
        int bcount;
        atomic_t generation;
        struct path *lower_paths;
};

Členská proměnná lower_paths je ukazatel na pole struktur cesty (které obsahují ukazatele jak na dentry, tak na strukturu mnt) pro každý adresář se stejnou cestou v souborových systémech níže. Například pokud bychom měli tři větve a dvě z nich obsahovaly adresář pojmenovavý "/foo/bar", pak prohledávání tohoto adresáře alokuje (1) strukturu dentry, (2) strukturu unionfs_dentry_info s polem lower_paths o třech položkách a (3) dvě struktury dentry pro adresáře. Dvě pozice v lower_paths potom budou obsahovat ukazatel na tyto záznamy v adresáři [dentry] a jejich odpovídající struktury mnt. Pole samotné se alokuje dynamicky, zvětšuje se a zmenšuje podle počtu větví. Počet větví (a tím pádem i velikost pole) je omezen konstantou zadanou v době překladu – UNIONFS_MAX_BRANCHES – jejíž výchozí hodnota je 128, což je asi tak o 126 více, než je běžně zapotřebí, a více než dost pro každé rozumné použití sjednocujících souborových systémů. Zbytek datových struktur unionfs – superbloky, záznamy v adresáři [dentry] atd. jsou velmi podobné struktuře výše.

VFS volá rutiny unionfs pro inody, záznamy v adresáři atd. přímo, unionfs poté zpětně volá VFS, aby provedl operace s odpovídajícími datovými strukturami na nižších větvích. Jako příklad uvedeme zápis do souboru: VFS zavolá funkci write() z vektoru operací se soubory inodu. Inode je inode unionfs, proto se zavolá unionfs_write(), který najde inode na nižší úrovni a zjistí, jestli je uložen na větvi pouze pro čtení. (Unionfs kopíruje výše při prvním zápisu do dat nebo metadat souboru, ne při prvním open() se oprávněním k zápisu.) Pokud je soubor uložen na větvi pouze pro čtení, unionfs najde zapisovatelnou větev a vytvoří na ní nový soubor (a všechny adresáře v cestě, které na zvolené větvi ještě neexistují). Potom zkopíruje různé atributy – čas modifikace a přístupu k souboru, vlastníka, režim, rozšířené atributy atd. – a samotná data souboru. Nakonec zavolá nízkoúrovňovou souborovou operaci write() z nově alokovaného inodu a vrátí výsledek zpět VFS.

Unionfs podporuje několik zapisovatelných větví. Smazání (unlink) souboru se propaguje přes všechny zapisovatelné větve a mažou se (dekrementuje se počet odkazů) všechny soubory stejného jména. Když unionfs narazí na větev pouze pro čtení, vytvoří vybělovací záznam o větev výš. Bělící záznam je pojmenován .wh.<filename>; adresář je označen záznamem pojmenovaným .wh.__dir_opaque.

Unionfs poskytuje určitou úroveň koherence cache tím, že ověřuje záznamy v adresáři před prací s nimi. To funguje vcelku dobře, dokud jdou všechny přístupy do souborových systémů v připojení přes unionfs. Přímé změny v souborových systémech níže jsou možné, ale unionfs toto neumí ve všech případech konkrétně ošetřit, obzvláště když se mění struktura adresářů.

Unionfs se aktivně vyvíjí. Podle dokumentu o návrhu verze 2 budou vybělení přesunuta do malého externího souborového systému. Soubor přemapování inodů na externím souborovém systému umožní vracet přetrvávající a stabilní čísla inodů, díky kterým se NFS exporty souborových systémů unionfs budou chovat korektně.

Stav unionfs jako kandidáta pro začlenění do hlavní řady linuxového jádra je smíšený. Na jednu stranu Andrew Morton začlenil unionfs do stromu -mm v lednu 2007 s tím, že unionfs nemusí být ideální řešení, ale je to jediné řešení skutečného problému. Začlenění do -mm také může povzbudit vývojáře, kterým se návrh nelíbí, k práci na jiných způsobech sjednocování. Na druhou stranu má ale unionfs silné NACK, mezi jinými od Ala ViroChristopha Hellwiga; a Linusovi se nechce se stavět proti názoru správců subsystému.

Mezi hlavní námitky proti unionfs patří silná duplikace datových struktur, jako jsou inody, obtíže s šířením operací z jedné větve do jiné, několik pravděpodobně neřešitelných souběhů [race condition] a celková velikost a složitost kódu. Tyto námitky se více či méně vztahují i na další skládatelné souborové systémy, jako je ecryptfs. Konsenzus na 2009 Linux file systems workshop byl takový, že skládatelné souborové systémy jsou koncepčně elegantní, ale se současnou strukturou VFS obtížně nebo nemožně implementovatelné udržovatelným způsobem. Vlastní zkušenost autorky článku s psaním skládatelného souborového systému (jaderný prototyp chunkfs) ji vede k tomu, že s touto kritikou souhlasí.

Skládatelné souborové systémy mohou být na cestě do zapomnění. Dustin Kirkland navrhl pro šifrované souborové systémy nový design, který by nebyl založen na skládatelných souborových systémech. Místo toho by vytvořil obecnou knihovnu funkcí ve VFS, které by poskytly vlastnosti, jež by byly užitečné i pro další souborové systémy. Identifikovali jsme několik specifických míst, kde by bylo možné sdílet kód mezi btrfs, NFS a navrhovaným designem ecryptfs. A je zjevné, že jestliže skládatelné souborové systémy nebudou součástí Linuxu, je budoucnost souborového systému založeného na skládání nejistá.

aufs

Aufs, což je zkratka pro Another UnionFS (Další UnionFS), byl původně implementován jako fork kódu unionfs, ale roku 2006 byl přepsán od základu. Hlavním vývojářem je Junjiro R. Okajima, další příspěvky přišly i od jiných. Hlavní webová stránka aufs je http://aufs.sourceforge.net/.

Architektura aufs je velmi podobná unionfs. Základním stavebním kamenem je pole struktur souborových systémů, které visí z objektu aufs na nejvyšší úrovni. Vybělení jsou pojmenována podobně jako v unionfs, ale jsou to pevné odkazy [hard link] na jediný vybělovací inode v lokálním adresáři. (Když je dosaženo maximálního počtu odkazů, je alokován další vybělovací inode.)

Aufs má ze sjednocujících souborových systémů nejvíce vlastností. Podporuje politiky pro několik zapisovatelných větví, z nichž nejužitečnější je pravděpodobně politika alokovat na větvi, na které je nejvíce volného místa. Aufs podporuje stabilní a přetrvávající čísla inodů pomocí tabulky překladu inodů, která je uložena na externím souborovém systému. Pevné odkazy mezi větvemi fungují. Obecně, pokud existuje více způsobů, jak něco udělat, aufs nejenom že implementuje všechny, ale také poskytuje konfigurační volbu, kterou za běhu určit, která z možností se použije.

Vzhledem k neuvěřitelné flexibilitě a počtu vlastností aufs, jak je možné, že není populárnější? Rychlé shlédnutí zdrojového kódu poskytuje odpověď: aufs sestává z přibližně 20 000 řádek hustého, nečitelného, nekomentovaného kódu, což je v kontrastu s okolo 10 000 řádky unionfs, 3 000 sjednocujících připojení a 60 000 celého VFS. Kód aufs je obecně něco, na co se nikdo nechce koukat.

Evoluce zdrojové základny aufs má tendence pohybovat se k rostoucí složitosti; například pokud odstranění adresáře plného vybělení trvá příliš dlouho, řešením je vytvořit jaderné vlákno, které vybělení odstraní na pozadí, místo aby se zkoušela najít efektivnější cesta, jak vybělení řešit. Aufs vaří, peče, smaží, ale dělá to způsobem, který znečišťuje jmenný prostor. Více v tomto případě není lépe; obecný trend je, že čím méně řádek kódu (a vlastností) sjednocující souborový systém má, tím lepšího přijetí se dočká od ostatních vývojářů souborových systémů.

Junjiro Okajima nedávno zaslal poněkud ořezanou verzi aufs pro hlavní řadu:

Mám další verzi, která se zbavila mnoha vlastností a velikost se zmenšila přibližně na polovinu, protože takový návrh se objevil na LKML. Ale nedostal jsem na to žádnou odpověď. Navíc se obávám, že ve skutečném světě je k ničemu, protože zahozené vlastnosti jsou nezbytné.

I když je aufs používán mnoha praktickými projekty (jako je například Live CD Knoppix), nevykazuje žádné známky toho, že by se blížilo k začlenění do hlavní řady.

Budoucnost vývoje sjednocujících souborových systémů

Poznámka: Autorka článku aktivně pracuje na sjednocujících připojeních, takže její shrnutí bude tomuto řešení nadržovat.

Sjednocující souborové systémy mají výhodu v tom, že většina sjednocujícího kódu pracuje na svém vlastním písečku – modularita je dobrá. Je ale obtížné implementovat efektivní operace souborového systému bez souběhů bez aktivní spolupráce s VFS. Názor autorky článku je takový, že dominujícím řešením sjednocování souborových systémů se stanou sjednocující připojení. Sjednocující připojení byla vždy u správců VFS populárnější a během diskuze o VFS na nedávném File systems workshop byli Jan Blunck a autorka článku schopni uspokojivě zodpovědět všechny otázky Ala Vira ohledně okrajových případů u sjednocujících připojení.

Částí toho, proč jsou sjednocující připojení přitažlivá, je, že jsme se zaměřili na specifické možnosti nasazení a zahodili všechny vlastnosti, které mají malý poměr užitečnost ku nákladům na údržbu. Řekli jsme ne NFS exportu sjednocujícího souborového systému a neimplementovali tedy stabilní čísla inodů. I když by NFS exporty byly hezké, nejsou u nejčastějších nasazení klíčovým požadavkem a jejich implementace by vyžadovala dvojitou strukturu inodů ve stylu skládatelného souborového systému s odpovídající složitostí šíření operací souborového systému nahoru a dolů mezi inodem sjednoceného připojení a inodem souborového systému v tomto připojení. Neřešíme online změnu jiných větví než horní, ani modifikace souborového systému, které neprobíhají přes kód sjednocujících připojení, takže nemusíme řešit komplexní záležitosti s koherencí cache. Aby se tato politika vynutila, navrhl Al Viro příznak superbloku ne, do tohoto souborového systému OPRAVDU nelze nijak zapisovat, protože v současnosti jsou povolení ke čtení/zápisu založena na jednotlivých připojeních.

Pole st_devst_ino ve stat se změní po zápisu do souboru (technicky při otevření s právem k zápisu), ale většina programů používá tuto informaci společně s ctime/mtime k rozhodnutí, jestli se soubor změnil – což je přesně to, co se stalo, takže aplikace by se měly chovat podle očekávání. Soubory z různých zařízení pod spojením ve stejném adresáři by mohly programy v uživatelském prostoru zmást, pokud očekávají, že je přejmenovávání v daném adresáři možné – například minimálně některé verze make menuconf se v takové situaci zblázní. Tento problém nicméně existuje u vázaných připojení [bind mount], které také mohou způsobit výskyt záznamů na různých úložných zařízeních ve stejném adresáři. Přepsání několika programů, které toto nezvládnou správně, je nutné k tomu, aby mohly fungovat s již existujícími vlastnostmi Linuxu.

Změny v daném souborovém systému pouze pro čtení musí být prováděny offline – když není připojen jako součást sjednocení. Máme nejméně dvě schémata, jak tyto změny přenést do zapisovatelné větve, které mohou mít adresáře označeny jako neprůhledné a my skrz ně nyní opět chceme vidět. Další varianta je použít informace mtime/ctime adresářů a zjistit z nich, jestli se adresář níže změnil od té doby, kdy jsme naposledy kopírovali jeho záznamy výše. To lze udělat inkrementálně za běhu.

Ve shrnutí jsou sjednocená připojení řešením, které se jaderným vývojářům líbí nejvíce. Pokud budeme schopni vyřešit problém s readdir() – a myslím si, že ano – pak budeme na cestě k začlenění v rozumném čase.

Související články

Sjednocující souborové systémy: Architektura, vlastnosti a návrhové volby
Sjednocující souborové systémy: Implementace, část 1.
Sémantické patchování pomocí nástroje Coccinelle
SystemTap, linuxová odpověď na DTrace

Odkazy a zdroje

Union file systems: Implementations, part II

Další články z této rubriky

LLVM a Clang – více než dobrá náhrada za GCC
Ze 4 s na 0,9 s – programovací jazyk Vala v praxi
Reverzujeme ovladače pro USB HID zařízení
Linux: systémové volání splice()
Programování v jazyce Vala - základní prvky jazyka

ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.