Na čem aktuálně pracují vývojáři GNOME a KDE Plasma? Pravidelný přehled novinek v Týden v GNOME a Týden v KDE Plasma.
Před 25 lety zaplavil celý svět virus ILOVEYOU. Virus se šířil e-mailem, jenž nesl přílohu s názvem I Love You. Příjemci, zvědavému, kdo se do něj zamiloval, pak program spuštěný otevřením přílohy načetl z adresáře e-mailové adresy a na ně pak „milostný vzkaz“ poslal dál. Škody vznikaly jak zahlcením e-mailových serverů, tak i druhou činností viru, kterou bylo přemazání souborů uložených v napadeném počítači.
Byla vydána nová major verze 5.0.0 svobodného multiplatformního nástroje BleachBit (GitHub, Wikipedie) určeného především k efektivnímu čištění disku od nepotřebných souborů.
Na čem pracují vývojáři webového prohlížeče Ladybird (GitHub)? Byl publikován přehled vývoje za duben (YouTube).
Provozovatel čínské sociální sítě TikTok dostal v Evropské unii pokutu 530 milionů eur (13,2 miliardy Kč) za nedostatky při ochraně osobních údajů. Ve svém oznámení to dnes uvedla irská Komise pro ochranu údajů (DPC), která jedná jménem EU. Zároveň TikToku nařídila, že pokud správu dat neuvede do šesti měsíců do souladu s požadavky, musí přestat posílat data o unijních uživatelích do Číny. TikTok uvedl, že se proti rozhodnutí odvolá.
Společnost JetBrains uvolnila Mellum, tj. svůj velký jazykový model (LLM) pro vývojáře, jako open source. Mellum podporuje programovací jazyky Java, Kotlin, Python, Go, PHP, C, C++, C#, JavaScript, TypeScript, CSS, HTML, Rust a Ruby.
Vývojáři Kali Linuxu upozorňují na nový klíč pro podepisování balíčků. K původnímu klíči ztratili přístup.
V březnu loňského roku přestal být Redis svobodný. Společnost Redis Labs jej přelicencovala z licence BSD na nesvobodné licence Redis Source Available License (RSALv2) a Server Side Public License (SSPLv1). Hned o pár dní později vznikly svobodné forky Redisu s názvy Valkey a Redict. Dnes bylo oznámeno, že Redis je opět svobodný. S nejnovější verzí 8 je k dispozici také pod licencí AGPLv3.
Oficiální ceny Raspberry Pi Compute Modulů 4 klesly o 5 dolarů (4 GB varianty), respektive o 10 dolarů (8 GB varianty).
Byla vydána beta verze openSUSE Leap 16. Ve výchozím nastavení s novým instalátorem Agama.
Současné vývojové jádro 2.6 je 2.6.31-rc1 vydané Linusem 24. června. Je toho tady hodně, ale rád bych teď, když začleňovací okno skončilo, řekl, že jsem se nikdy necítil lépe při začleňování věcí od ostatních. Doufám, že to není jenom náhoda, ale že lidé udržovali své gitové stromy čisté. A i když bylo několik případů, kdy jsem řekl „ne, tohle začleňovat nebudu“, celkově pro mě toto začleňovací okno bylo opravdu bezbolestné. Linus řekl, že pravděpodobně ještě začlení architekturu S+Core (score), protože ta se mimo svůj strom dotýká pouze souboru MAINTAINERS. Významné změny v 2.6.31 zahrnují čítače výkonnosti, znaková zařízení v uživatelském prostoru (CUSE), jaderné nastavování režimu [Kernel Mode Setting] pro hardware Radeon, kmemleak a kmemcheck, fsnotify (které poskytuje společnou implementaci dnotify a inotify) společně s ohromným množstvím různých druhů ovladačů. Všechny detaily vizte v kompletním changelogu.
Během tohoto týdne nevyšly žádné aktualizace stabilního jádra a žádné patche se ani nerevidují.
-- Matthew Garret (stojí za to přečíst si celý článek)
-- Greg Kroah-Hartman informuje o stromu -staging
Od článku z minulého týdne bylo do hlavní řady jádra začleněno okolo 2050 neslučovacích sad změn, celkem bylo tedy do 2.6.31 začleněno 8288 změn. Začleňovací okno skončilo, takže všechny hlavní vlastnosti 2.6.31 (s možnou výjimkou u architektury S+Core) by měly být začleněné. Zajímavé změny z minulého týdne viditelné pro uživatele zahrnují:
Do architektury MIPS byla přidána podpora pro hugetlbfs, stejně jako podpora pro hibernaci (pouze pro jednoprocesorové systémy).
Alokátor stránek SLUB přidal printk() s novou diagnostickou informací pro ladění situací nedostatku paměti.
Byl přidán soubor /proc/softirqs, který ukazuje počet softwarových přerušení pro každé CPU. Do /proc/stat byl také přidán řádek „softirq“.
Infrastruktura pro profilování gcov pro testování pokrytí kódu byla začleněna. Přidává funkce vyžadované profilujícím kódem a podporu kbuild pro překlad jader s profilováním gcov společně s rozhraním debugfs pro získávání získaných dat.
Bylo přidáno API pro zařízení PPS (pulzů za sekundu). Jedná se o zařízení, která poskytují vysoce přesný signál, který lze využít k opravování systémového času.
Bylo přidáno ioctl() EXT4_IOC_MOVE_EXT pro podporu online defragmentace ext4.
Sysfs rozhraní pro přidávání ovladačů I2C, které zaujímá místo různých parametrů modulů force_*, bylo začleněno.
Byl přidán kontrolor proudu příkazů pro hardware Radeon r3xx-r5xx, aby se procesům v uživatelském prostoru zabránilo přistupovat k paměti, která jim nepatří.
Do perf tool přibylo několik vlastností včetně čistého datového výstupu stejně jako profilování grafu volání.
Do architektury PowerPC byla přidána podpora pro čítače výkonnosti.
Nyní lze bootovacím parametrem ecrc povolit či zakázat CRC kontrolu PCI end-to-end (ECRC).
Byl začleněn softwarový vkladač chyb pro PCI Express Advanced Error Reporting (AER).
Jako experimentální vlastnost byl přidán klient NFS verze 4.1. Podpora pro server zatím není začleněna.
Firewire (IEEE 1394) má nyní podporu pro IPv4 síťování.
Nové ovladače zařízení:
Architektury/procesory/systémy:
Síť: ethernetové zařízení Xtensa S6105 GMAC.
Vstupní zařízení:
Různé:
Staging:
Mezi změny viditelné pro vývojáře jádra patří:
Poměrně hodně kódu odstraňujícího Velký jaderný zámek (Big Kernel Lock, BKL) bylo začleněno do stromu fs/. Nyní jsou všechny super_operations a address_space_operations volány bez držení BKL.
IRQF_SAMPLE_RANDOM, které určuje, jestli lze přerušení od ovladače využít jako zdroj entropie, bylo vloženo do plánu na odstranění vlastnosti.
Infrastruktura pro ladění paměti DRM byla odstraněna. Nepoužívala se roky a chtít po uživateli říci, kolik paměti je uvolňováno, je recept na pohromu, i kdyby se to někdy použilo.
David Miller je nyní správcem subsystému IDE, který převzal po Bartlomieji Zolnierkiewiczovi po přátelské dohodě. David plánuje přechod IDE do režimu údržby.
SCSI device information matching získalo podporu pro několik tabulek blacklistů.
Testovací prostředky v jbd2 a ext4 byly zkonvertovány z jaderných značek [kernel marker] na sledovací body [tracepoint].
OCFS2 získalo podporu pro lockdep tím, že byly přidány správné poznámky pro lockdep pro všechny zámku v clusteru kromě těch, které jsou místo pro proces získávány pro uzel.
Seznam pro řízení přístupu [access control list, ACL] je nyní v některých souborových systémech (jfs, ext2, ext3, ext4, jffs2, btrfs, reiserfs, nilfs2, xfs) cachován ve struct inode..
Vzhledem k tomu, že se začleňovací okno uzavřelo, je dalším krokem stabilizace. Něco okolo 3000 či více změn si pravděpodobně najde cestu do hlavní řady před vydáním 2.6.31, ke kterému by mělo dojít koncem srpna nebo začátkem září.
Originál tohoto článku pro LWN napsal Goldwyn Rodrigues.
Mnoho embedded systémů obsahuje blok nonvolatilní RAM (NVRAM) oddělený od běžné systémové paměti. Nedávný patch, který zaslal Marco Stornelli, je souborový systém pro tento druh paměti, kam zařízení může zapisovat data, ke kterým se často přistupuje (například adresář v mobilním telefonu). Chráněný RAMFS (PRAMFS) chrání souborový systém založený na NVRAM před zbloudilými nebo mylnými zápisy do chráněné části RAM, které způsobily chyby jádra. Protože je uložen v NVRAM, přežívá souborový systém reboot a díky tomu může také být využit k uchování důležitých informací o pádu.
PRAMFS je robustní, co se týče chybných zápisů do chráněné oblasti, ke kterým by mohlo dojít kvůli chybám jádra. Záznamy v tabulce stránek, které mapují přetrvávající RAM, jsou při inicializaci označeny jako pouze pro čtení. Zápisové operace do souborového systému tyto stránky dočasně označí jako zapisovatelné, zápis je proveden a pte (page table entry, záznam v tabulce stránek – pozn. překl.) opět označen jako pouze pro čtení, přičemž je držen zámek. Toto omezení omezuje zápisy do souborového systému na dobu, kdy jsou drženy zámky. Tuto ochranu proti zápisu lze vypnout jadernou konfigurační volbou CONFIG_PRAMFS_NOWP.
PRAMFS vynucuje u všech souborů používání přímého IO. Když je soubor otevřen, filp->flags se nastavuje na O_DIRECT. Otevíráním všech souborů s O_DIRECT se obchází cachování stránek a data jsou na úložné zařízení ukládána okamžitě. Rychlostně to téměř odpovídá rychlosti RAM, ale nutí to aplikace zapisovat se zarovnáním na bloky [block-aligned I/O].
PRAMFS nemá obnovovací nástroje jako například žurnálování, díky kterým by mohl přežít pád nebo výpadek napájení při operaci zápisu. Souborový systém udržuje kontrolní součet superbloku a inodů, kterými ověřuje platnost uloženého objektu. Inode s neplatným kontrolním součtem je označen jako špatný, což může v případě výpadku napájení při zápisu vést ke ztrátě dat.
PRAMFS také podporuje vykonávání na místě (execute in place, XIP), což je technika, která umožňuje spustit program přímo z úložiště bez nutnosti kopírovat ho do systémové RAM. U souborového systému v RAM to má smysl, protože z úložného zařízení lze program vykonávat stejně rychle, jako ze systémové RAM, přičemž navíc nedochází k vytváření duplikátu.
Pro vytvoření PRAMFS neexistuje žádný nástroj, souborový systém je automaticky vytvořen, když je připojen s volbou init. Příkaz, který vytvoří a připojí PRAMFS je:
# mount -t pramfs -o physaddr=0x20000000,init=0x2F000,bs=1024 none /mnt/pram
Tento příkaz vytvoří souborový systém o velikosti 0x2F000 bytů s velikosti bloku 1024 bytů a umístí ho na fyzickou adresu 0x2000000.
Existující souborový systém lze připojit zadáním stejného parametru physaddr, který byl použit v předchozím připojení. Detaily o souborovém systému jako velikost bloku a souborového systému jsou načteny ze superbloku:
# mount -t pramfs -o physaddr=0x20000000 none /mnt/pram
Další parametry souborového systému jsou:
bpi: Specifikuje poměr bytů/inode [bytes-per-inode]. Pro každých bpi bytů v souborovém systému je vytvořen inode.
N: Specifikuje počet inodů, které mají být alokovány pro tabulku inodů. Jestliže tato volba není použita, vypočítá se počet inodů z poměru bytů/inode.
Pokud není specifikována volba init, volby bs, bpi a N se ignorují, protože se daná informace načítá z existujícího souborového systému. Když je souborový systém vytvářen a není specifikována hodnota pro rezervaci inodů, ve výchozím nastavení souborový systém rezervuje pro tabulku inodů 5 % z místa v souborovém systému.
Pro test ochrany paměti v PRAMFS vývojáři napsali jaderný modul, který se pokouší zapsat do paměti PRAMFS se záměrem poškodit paměťový prostor. To vede k protection fault v jádře [kernel protection fault] a po rebootu lze souborový systém znovu připojit a zjistit, že modul nebyl schopen souborový systém poškodit.
PRAMFS má jednoduché rozvržení, superblok se nachází v prvních 128 bytech bloku RAM, následován tabulkou inodů, mapou využití bloků a nakonec datovými bloky. Superblok je 128 bytů dlouhý a obsahuje všechny důležité informace potřebné pro připojení souborového systému jako jeho velikost, velikost bloku atd.
Tabulka inodů se skládá z inodů potřebných pro souborový systém. Počet inodů je vypočítán, když je souborový systém inicializován. Každý inode je 128 bytů dlouhý, v inodech jsou obsaženy adresářové záznamy, jako je jméno souboru a vlastnící inode. To představuje problém pro pevné odkazy [hard link], protože pevný odkaz potřebuje pro stejný inode dva záznamy v adresáři v různých adresářích – PRAMFS tedy pevné odkazy nepodporuje. Formát inodů také omezuje jméno souboru na 48 znaků. Číslo inodu je pořadové číslo daného inodu od začátku souborového systému.
Běžný inode souboru v PRAMFS obsahuje pole i_type.reg.row_block, které ukazuje na datový blok, který obsahuje dvojitě nepřímý ukazatel na datové bloky souboru. To je podobné poli pro dvojité nepřímé ukazatele v inodu souborového systému ext2, znamená to ale, že soubor menší než 1 blok vyžaduje ke svému uložení 3 bloky.
Inody v adresáři jsou spojeny obousměrným spojovým seznamem. Inode adresáře ukládá první a poslední inode výpisu adresáře, předchozí záznam prvního inode a další záznam posledního inode jsou ukončeny nulou.
PRAMFS využívá stránkovací jednotku systému a mapuje svoji RAM jako pouze pro čtení. Zápisy do datových objektů nejprve označí příslušné záznamy v tabulce stránek jako zapisovatelné, pak se provede zápis a stránky se opět označí jako pouze pro čtení. Tato operace se provádí atomicky při držení spinlocku tabulky stránek a se zakázanými přerušeními. Po zápisu jsou vyřazeny zastaralé záznamy v TLB. Zápisové zámky lze držet na úrovni superbloku, inodu a bloku v závislosti na granularitě změny.
Vzhledem k tomu, že se PRAMFS pokouší vyhnout poškození souborového systému způsobenému chybami v jádře, sdílené oblasti mmap() lze pouze číst. Špinavé stránky v cache stránek nelze zapsat zpět na souborový systém. Z tohoto důvodu PRAMFS definuje pouze členskou proměnnou readpage() struct address_space_operations; záznam writepage() je deklarován jako NULL.
Toto je druhý pokus dostat PRAMFS do hlavní řady. Naposledy se v roce 2004 pokoušel Steve Longerbeam z Montavista.
Domovská stránka PRAMFS tvrdí, že souborový systém má kompletní vlastnosti. V diskuzi na linux-kernel s tím nicméně Henrique de Moraes Holschuh silně nesouhlasil:
Zatím není dostatek výkonnostních benchmarků pro porovnání s jinými souborovými systémy, aby bylo možné vytvořit názor. Výkonnostní testy provedené, když bylo přidáno vykonání na místě, ukazují nízkou výkonnost 13 Mbps při zápisu po znacích a 35 Mbps při blokových zápisech. Pavel Machek tato čísla považuje za poměrně malá, obzvláště pro souborový systém založený na RAM:
Nebyl proveden žádný test, který by používal existující řešení, jako je ramdisk, na stejném hardware, aby se porovnávala jablka s jablky. Nízký výkon je přisuzován nadměrnému zamykání při zápisech. Pavel věří, že vývojáři mají zmatené cíle, co se návrhu souborového systému týče – jestli navrhují pro rychlost, kompletnost, nebo robustnost.
PRAMFS je souborový systém, který vyplňuje prázdné místo většinou u embedded zařízení s NVRAM, a proto postrádá důležité vlastnosti, jako jsou pevné odkazy a sdílené mmap(). Pro značné množství situací se však celý souborový systém zdá být přehnaný. Pavel považuje za lepší možnost vytvořit zvláštní blokové zařízení založené na NVRAM s tradičním souborovým systémem nebo souborový systém založený na souborovém systému určeném pro SSD (Solid State Device, zařízení bez rotujících částí). Se současným počtem námitek se PRAMFS do hlavní řady pravděpodobně nedostane. Marco nicméně plánuje kód dále vylepšovat dalšími vlastnostmi a aktualizovat domovskou stránku PRAMFS, aby lépe popisovala jeho cíle.
Originál tohoto článku pro LWN.net napsal Neil Brown.
V tomto posledním článku se podíváme pouze na jeden návrhový vzor. Začali jsme jemnými detaily čítání odkazů a pak oddálili pohled a podívali se na celé datové struktury. Nyní se přesuneme k ještě širší perspektivě navrhování subsystémů. Jako každý vzor i tento potřebuje jméno a pracovní název je zde „chyba mezivrstvy“ [midlayer mistake]. Zní to jako antivzor, protože se zdá, že popisuje něco, čemu je potřeba se vyhýbat. I když je to pravda, je to také velmi silný vzor s pevně předepsanými pravidly. Když vidíte „mezivrstvu“, víte, že jste v cílové oblasti tohoto vzoru a že je čas zjistit, jestli se na vás tento vzor vztahuje a chce vás vést jiným směrem.
Ve světe Linuxu se termín „mezivrstva“ zdá (podle názoru autora článku a také podle Google cache) být nejvíce spojen s SCSI. „SCSI mezivrstva“ si před několika lety prožila špatné chvilky a v relevantních konferencích se vedlo mnoho debat o tom, proč selhala. Bylo to sledování těchto diskuzí, co poskytlo základ, ze kterého nakonec pomalu nabyl formu tento vzor.
Termín „mezivrstva“ zjevně implikuje „horní vrstvu“ a „spodní vrstvu“. V tomto kontextu je „horní“ vrstva kód, který se vztahuje k mnoha subsystémům. Může to být vrstva systémových volání POSIX, která podporuje všechna systémová volání, bloková vrstva, která podporuje všechna bloková zařízení, či VFS, která podporuje všechny souborové systémy. Bloková vrstva by byla horní vrstvou v příkladu s „SCSI mezivrstvou“. „Spodní“ vrstva je potom konkrétní implementace nějaké služby. Může to být specifické systémové volání, ovladač pro specifický kus hardwaru nebo specifický souborový systém. Ovladače pro různé SCSI řadiče jsou spodní vrstvou pro SCSI mezivrstvu. Krátký pohled na tento seznam příkladů ukazuje, že pozice, kterou kus kódu zaujímá, z velké části záleží na perspektivě – pro VFS je daný souborový systém částí spodní vrstvy, pro blokové zařízení je stejný souborový systém částí horní vrstvy.
Mezivrstva sedí mezi horní a spodní vrstvou. Přijímá požadavky z horní vrstvy, provede nějaké zpracování společné implementacím spodní vrstvy a předzpracované požadavky poté předá níže – pravděpodobně hodně zjednodušené a specifické pro doménu – relevantnímu ovladači. To poskytuje jednotnost implementace, sdílení kódu a značně zjednodušuje úkol implementace ovladače pro spodní vrstvu.
Základem teorie o „chybě mezivrstvy“ je to, že mezivrstvy jsou špatné a neměly by existovat. Společná funkcionalita, u které je tak velké pokušení vložit ji do mezivrstvy, by místo toho měla být poskytnuta knihovnou funkcí, které lze použít, doplnit nebo ignorovat v každém ovladači spodní vrstvy nezávisle. Každý subsystém, který podporuje několik implementací (nebo ovladačů), by měl poskytnout velmi tenkou horní vrstvu, která volá ovladače ve spodní vrstvě přímo, a bohatou knihovnu podpůrného kódu, který zjednodušuje implementaci těchto ovladačů. Tato knihovna je potom ovladačům k dispozici, ale není jim nucena.
Abychom tento vzor osvětlili, prozkoumáme tři různé subsystémy a podíváme se, jak se na ně tento vzor vztahuje specificky – na blokovou vrstvu, VFS a raid vrstvu ,md‘ (tj. na oblasti, se kterými je autor nejlépe obeznámen).
Největší částí práce blokové vrstvy je přebírat požadavky na ,čtení‘ a ,zápis‘ pro bloková zařízení a předat je odpovídajícím ovladačům na spodní úrovni. To zní jednoduše. Zajímavý bod je v tom, že většina blokových zařízení má něco společného s rotujícími médii a rotující média těží z toho, když jsou po sobě jdoucí požadavky blízko u sebe v adresovém prostoru – to pomáhá omezit dobu posunu hlaviček. I média bez rotujících částí mohou těžit z toho, že jsou požadavky na sousedících adresách, protože je možné sloučit je do menšího počtu větších požadavků. Mnoho blokových zařízení tedy může těžit z toho, že všechny požadavky prochází přes výtahový algoritmus, který je řadí podle adresy, aby se zařízení lépe využívalo.
Je velmi lákavé implementovat výtahový algoritmus jako „mezivrstvu“, tj. vrstvu těsně pod horní vrstvou. To je přesně to, co Linux dělal ve dnech jader 2.2 a starších. Požadavky přicházely do ll_rw_block() (horní vrstva), která provedla základní kontrolu správnosti a inicializovala některé pole pro interní použití, poté požadavky předala do make_request() – srdce výtahu. Nicméně ne každý požadavek přecházel do make_request(); pro „md“ zařízení byla výjimka, tyto požadavky byly předávány md_make_request(), která dělala něco úplně jiného, jak je to vhodné pro RAID zařízení.
Zde je první důvod pro to nemít mezivrstvy v oblibě – objevují se v nich zvláštní případy. Když se píše mezivrstva, je nemožné předvídat všechny možné potřeby, které může mít ovladač ve spodní vrstvě, takže není možné mít je v mezivrstvě všechny. Mezivrstvu by bylo možné přepracovat pokaždé, když se objeví nová potřeba, ale to by pravděpodobně nebylo nejefektivnější využití času. Místo toho má počet zvláštních případů tendenci narůstat.
Dnešní bloková vrstva je v mnoha ohledech podobná tomu, jaká byla v době, když byl výtah velmi centrální. Samozřejmě se změnila spousta detailů a plánování I/O požadavků je mnohem sofistikovanější, nicméně stále je zde značná rodová podoba. Jeden významný rozdíl (pro naše účely) je existence blk_queue_make_request(), kterou musí volat všechny ovladače blokových zařízení buď přímo, nebo nepřímo přes blk_init_queue(). V té se registruje funkce podobná make_request() či md_make_request() z 2.2, která se má volat pro obsluhu každého IO požadavku.
Tento malý přídavek efektivně mění výtah z mezivrstvy, která je vnucena každému zařízení v systému, na knihovní funkcí, která je k dispozici zařízením. To byl významný krok správným směrem, pro ovladače je nyní mnohem jednodušší zvolit si možnost, že výtah používat nebudou. To dělají všechny ovladače virtuálních zařízení (md, dm, loop, drbd atd.), a dokonce i některé ovladače pro fyzický hardware (např. umem) dodávají svou make_request_fn().
I když výtah rozhodně přestal být mezivrstvou, stále si v mnoha ohledech ponechává její vzhled. Jedním příkladem je struktura struct request_queue (definovaná v <linux/blkdev.h>). Tato struktura je skutečně součástí blokové vrstvy. Obsahuje pole, která jsou základní součástí blokového rozhraní, jako je ukazatel na funkci make_request_fn(), který jsme již zmínili. Mnoho dalších polí je nicméně specifických pro kód výtahu, například elevator (vybírá z různých IO plánovačů) a last_merge (používané ke zrychlení vyhledávání ve specifické frontě). Zatímco výtah může umístit pole do struct request_queue, ostatní kód musí použít ukazatel queuedata, do kterého se ukládají sekundární datové struktury.
Toto uspořádání je další indikátor pro mezivrstvu. Když primární datová struktura obsahuje ukazatel na podřízenou datovou strukturu, pravděpodobně máme mezivrstvu, která tuto primární datovou strukturu spravuje. Lepší uspořádání je použití vzoru „zabudované kotvy“, který jsme viděli v předchozím článku této série. Ovladač na spodní vrstvě by měl alokovat své datové struktury, které by obsahovaly datovou strukturu (nebo struktury) používanou knihovními funkcemi. struct inode je dobrý příklad tohoto přístupu, i když s jemně se lišícími detaily. Ve 2.2 struct inode obsahovalo sjednocení datových struktur specifických pro každý souborový systém a ukazatel (generic_ip) k využití jiným souborovým systémem. V jádře 2.6 je struct inode běžně zabudován do struktury inodu specifické pro souborový systém (i když je zde stále ukazatel i_private, který se zdá být nepotřebný).
Poslední ukazatel na mezivrstvu, kterou stále můžeme ve výtahu vidět, je tendence seskupovat nesouvisející kód. Návrh knihovny přirozeně poskytuje oddělenou funkcionalitu v oddělených funkcích a na ovladači ze spodní vrstvy nechává, aby volal to, co potřebuje. Mezivrstva jednoduše zavolá všechno, co by mohlo být potřeba.
Když se podíváme na _make_request() (vstupní bod výtahu ve 2.6), vidíme brzké volání blk_queue_bounce(). Tato funkce poskytuje podporu pro hardware, který nedokáže používat celý adresový prostor, když pomocí DMA přesouvá data mezi pamětí a zařízením. Aby se takové případy vyřešily, je někdy potřeba přesunout data do přístupnější paměti předtím, než jsou odeslána do zařízení, či přesunout je z této paměti poté, co byla přijata od zařízení. Tato funkcionalita na výtahu v podstatě nezávisí, ale přesto je nucena všem jeho uživatelům.
V blokové vrstvě a jejím vztahu se subsystémem výtahu vidíme, že výtah byl dříve implementován jako mezivrstva, ale od tohoto přístupu ustoupil a výtah je nyní zjevně volitelný. Stále obsahuje stopy svého dědictví, které nám posloužily jako dobrý příklad klíčových identifikátorů mezivrstvy: Kód je nižší vrstvě vnucován, v tomto kódu jsou ošetřovány zvláštní případy, datové struktury obsahují ukazatele na podřízené datové struktury a podpůrnou funkcí je volán další nesouvisející kód.
S tímto na mysli pokračujme.
VFS (virtuální souborový systém) je oblast bohatá na mezivrstvy a jejich alternativy. To je proto, že se souborové systémy v mnohém liší, mohou využívat mnoho užitečných služeb a bylo věnováno mnoho práce tomu, aby to všechno fungovalo dohromady efektivně a výkonně. Horní vrstva VFS je z větší části tvořena voláními funkcí vfs_, které poskytují vstupní body do VFS. Ty jsou volány různými funkcemi sys_, které implementují systémové volání, kódem nfsd který často přistupuje k souborovým systémům bez využívání systémových volání, a z několika dalších částí jádra, které potřebují pracovat se soubory.
Funkce vfs_ poměrně rychle volají přímo souborový systém, o který se jedná, přes jednu z mnoha struktur _operations, která obsahuje seznam ukazatelů na funkce. Tyto struktury jsou inode_operations, file_operations, super_operations atd. podle toho, se kterým objektem je manipulováno. Toto je přesně model, o kterém mluví vzor „chyba mezivrstvy“. Tenká horní vrstva volá přímo spodní vrstvu, která, jak uvidíme, k provedení svého úkolu značně využívá knihovní funkce.
Prozkoumáme a porovnáme dvě různé sady služeb poskytovaných souborovým systémům, cache stránek a cache záznamů v adresáři.
Souborové systémy obecně chtějí využívat dopředné načítání [read-ahead] a zpožděný zápis [write-behind]. Když je to možné, data by měla být z úložného zařízení načtena dřív, než jsou zapotřebí, aby byla k dispozici, když přijde požadavek je načíst; jakmile jsou přečtena, je vhodné si je někde ponechat pro případ, že by je bylo zapotřebí znovu, což je běžné. Podobně lze získat tím, že se zápisy trochu opozdí, aby se vyrovnalo využívání propustnosti zařízení a aplikace nemusely čekat na dokončení zápisu. Obě tyto vlastnosti jsou poskytovány cachí stránek, která je z větší části implementována v mm/filemap.c a mm/page-writeback.c.
Ve své nejjednodušší formě souborový systém poskytne cachi stránek objekt nazvaný address_space, který ve své address_space_operations obsahuje rutiny pro čtení a zápis jediné stránky. Cache stránek poté poskytuje operace, které lze použít jako file_operations a poskytnout tak abstrakci souboru, který má být předán horní vrstvě VFS. Pokud se podíváme na file_operations pro běžný soubor v ext3, vidíme:
const struct file_operations ext3_file_operations = { .llseek = generic_file_llseek, .read = do_sync_read, .write = do_sync_write, .aio_read = generic_file_aio_read, .aio_write = ext3_file_write, .unlocked_ioctl = ext3_ioctl, #ifdef CONFIG_COMPAT .compat_ioctl = ext3_compat_ioctl, #endif .mmap = generic_file_mmap, .open = generic_file_open, .release = ext3_release_file, .fsync = ext3_sync_file, .splice_read = generic_file_splice_read, .splice_write = generic_file_splice_write, };
Osm ze třinácti operací jsou obecné funkce poskytované cachí stránek. Ze zbývajících pěti dvě operace ioctl() a release() vyžadují implementaci specifickou pro souborový systém; ext3_file_write() a ext3_sync_file() jsou středně velké obalovací funkce okolo obecných funkcí poskytovaných cachí stránek. To je ztělesnění dobrého návrhu subsystému podle našeho vzoru. Cache stránek je dobře definovaná knihovna, kterou lze z větší části využívat tak, jak je (při čtení souboru na ext3), umožňuje souborovému systému přidávat funkce okolo různých vstupních bodů (jako ext3_file_write()) a lze ji jednoduše úplně ignorovat, když není relevantní (jako u sysfs či procfs).
I zde je malý prvek mezivrstvy vnucované spodní vrstvě, protože obecná struct inode obsahuje struct address_space, kterou používá jenom cache stránek a která je pro systémy, které cache stránek nevyužívají, irelevantní. Tuto malou odchylku od vzoru lze ospravedlnit jednoduchostí, kterou poskytuje, protože drtivá většina souborových systému cache stránek používá.
Stejně jako cache stránek poskytuje dcache souborovému systému důležitou službu. Ke jménům souboru se obvykle přistupuje několikrát, mnohem častěji než k obsahu souboru. Jejich cachování je tedy důležité a mít dobře navrženou cache záznamů v adresáři [directory entry cache] je důležitou součástí efektivního přístupu ke všem objektům v souborovém systému. Od cache stránek se však dcache v jedné významné věci liší: Není volitelná. Je nucena každému souborovému systému a efektivně se tedy jedná o „mezivrstvu“. Pochopení, proč to tak je a proč je to správně, je důležitou součástí hodnoty a aplikovatelnosti tohoto návrhového vzoru.
Jeden z argumentů pro vnucenou dcache je ten, že přejmenovávání adresářů má nějaké zajímavé souběhy; je snadné chybovat při jejich zvládání. Místo toho, aby měl každý souborový systém potenciál to udělat špatně, lze to vyřešit jednou provždy v dcache. Klasický příklad souběhu je, když je /a/x přejmenováváno na /b/c/x a zároveň /b/c přejmenováváno na /a/x/c. Pokud obě tato přejmenování uspějí, pak budou ,c‘ a ,x‘ obsahovat jeden druhý a budou odpojeny od zbytku adresářové struktury, což je situace, kterou nechceme.
Ochrana proti takovému souběhu není možná, pokud bychom cachovali záznamy v adresáři pouze na úrovni adresářů. Společný kód cache potřebuje přinejmenším být schopen vidět celý souborový systém, aby mohl detekovat možnou smyčku způsobující souběh. Udržování cache záznamů v adresáři na úrovni celých souborových systémů je tedy zjevně dobrý nápad a je rozumné lokálním souborovým systémům silně doporučovat používat ji. Ale jestli je dobrá volba vnutit ji všem souborovým systémům, to tak jasné není.
Síťové souborové systémy netěží z detekce smyček, kterou je schopna poskytnout dcache, protože je stejně potřeba všechno dělat na serveru. „Virtuální“ souborové systémy jako sysfs, procfs a ptyfs také cache moc nepotřebují, protože všechna jejich jména souborů jsou stejně neustále v paměti. Jestli dcache těmto souborovým systémům škodí, se nedá snadno říci, protože nemáme kompletní a optimalizovanou implementaci, která na dcache nezávisí a se kterou by se dalo porovnávat.
Z klíčových identifikátorů mezivrstvy, které jsme diskutovali výše, na cenu nejzjevněji ukazuje fakt, že se v mezivrstvě většinou množí kód pro zvláštní případy. Mělo by tedy být užitečné dcache prozkoumat a zjistit, jestli také trpí tímto problémem.
První zvláštní případ, který vidíme v dcache, leží mezi příznaky uloženými v d_flags. Dva z těchto příznaků jsou DCACHE_AUTOFS_PENDING a DCACHE_NFSFS_RENAMED. Každý je specifický pro jeden souborový systém – příznak AUTOFS se zdá být využíván pouze interně souborovým systémem autofs, takže to vlastně není skutečný zvláštní případ v dcache. NFS příznak je nicméně používán k rozhodování ve společném kódu dcache na několika místech, zjevně to tedy zvláštní případ je, i když ne nutně nějak obzvláště drahý.
Dalším místem, kde hledat kód pro zvláštní případy, je, když může být ukazatel na funkci ve struktuře _operations NULL, a NULL přitom znamená, že má být provedena nějaká specifická akce (místo neprovádění žádné akce). To se stává, když je přidána nová operace pro podporu některých zvláštních případů a NULL znamená ,výchozí‘ případ. Není to vždycky špatně, ale může jít o varovný signál.
Ve struktuře dentry_operations je několik funkcí, které mohou být NULL. d_revalidate() je příklad, který je poměrně neškodný. Jednoduše souborovému systému umožňuje otestovat, jestli je záznam stále platný a buď ho aktualizovat, nebo vyřadit. Souborové systémy, které tohle nepotřebují, jednoduše nedělají nic, protože volat funkci, která nic nedělá, je zbytečné.
Také nicméně najdeme d_hash() a d_compare(), které souborovému systému umožňují poskytnout nestandardní funkce pro hashování a porovnávání například pro jména souborů, ve kterých není rozdíl mezi malým a velkým písmenem. Toto hodně vypadá jako zvláštní případ, protože společný kód používá explicitní výchozí nastavení, když je ukazatel NULL. Jednotnější implementace by nutila každý souborový systém poskytnout nenulové d_hash() a d_compare(), přičemž mnoho souborových systémů by si vybralo z knihovny ty varianty, které rozlišují malá a velká písmena.
Dalo by se jednoduše argumentovat, že udělat to – vynutit volání funkce navíc pro hash a porovnávání na běžných souborových systémech – by představovalo zbytečný postih na výkonnosti, což je pravda. Nicméně vzhledem k tomu – proč je vhodné nutit tento postih souborovým systémům, které používají jiný standard?
Přístup podobnější knihovně by znamenal, že by VFS předala cestu souborovému systému a umožnila mu provést vyhledávání buď voláním obsluhy cache v knihovně, nebo použitím knihovních funkcí k zjištění součástí jména a vyhledáváním přímo ve svém vlastním uloženém stromu souborů.
Dcache je tedy zjevně mezivrstva a má tedy nějaké slabiny. Ze všech mezivrstev v jádře pravděpodobně nejlépe odpovídá pozorování zmíněnému výše, že by mohla být „přepracována pokaždé, když se objeví nová potřeba“. Dcache je neustále vylepšována, aby vyhověla potřebám nových souborových systémů. Jestli je to „efektivní využití času“, je debata pro jiné fórum.
Posledním příkladem v našem zkoumání mezivrstev a knihoven je ovladač md, který podporuje různé implementace softwarového RAIDu, a s ním spojený kód. md je zajímavý, protože je to směs vlastností podobných mezivrstvě a vlastností podobných knihovně, takže výsledek je trochu chaotický.
„Ideální“ návrh ovladače md by byl (podle vzoru „chyba mezivrstvy“) poskytnout spoustu užitečných funkcí, které by mohly mohly být používány nezávislými moduly pro jednotlivé úrovně RAID. Například RAID1 by tedy byl samostatný ovladač využívající knihovní podporu pro správu náhradních disků, provádění resynchronizace a čtení metadat. RAID0 by byl samostatný ovladač, který by používal stejný kód pro čtení metadat, ale kód pro správu náhradních disků ani resynchronizaci by mu k ničemu nebyl.
Tak to však bohužel není. Jedním z důvodů je to, jakým způsobem bloková vrstva dříve spravovala hlavní [major] a vedlejší [minor] číslo zařízení. Dnes je to mnohem flexibilnější, ale v minulosti různá hlavní čísla zařízení představovala unikátní ovladač a unikátní schéma pro rozvržení vedlejších čísel. Hlavní čísla byl omezený zdroj, takže mít různá pro RAID0, RAID1, RAID5 atd. by bylo plýtvání. Bylo tedy alokováno jenom jedno číslo (9) a jeden ovladač měl mít na starosti všechny úrovně RAID. Tato potřeba bezpochyby utvrdila názor, že mezivrstva, která bude obsluhovat všechny úrovně RAID, je správný přístup, a tento názor přetrval.
Byly učiněny nějaké malé krůčky směrem ke knihovnímu přístupu, ale ty jsou opravdu malé a nelze z nich činit závěry. Jeden jednoduchý příklad je funkce md_check_recovery(). Knihovní funkce je to v tom ohledu, že ji konkrétní implementace dané úrovně RAID musí explicitně zavolat, jinak se nepoužívá. Provádí nicméně několik nesouvisejících úloh, jako je aktualizace metadat, vyprázdnění bitové mapy chystaného zápisu [write-intent-bitmap], odstranění zařízení, která selhala, a (překvapivě) zjištění, jestli je potřeba obnova. Jako taková je malou součástí mezivrstvy, protože vnucuje zkombinování několika nesouvisejících úloh.
Lepší příklad je možná md_register_thread() a spol. Některá md pole potřebují jaderné vlákno, které poskytuje podporu pro některé funkce (jako je například plánování požadavků na čtení na jiné disky po selhání). md.c poskytuje knihovní funkce md_register_thread() a md_unregister_thread(), které může daná úroveň RAIDu volat podle potřeby. To je v pořádku. Ovladač md se nicméně občas rozhodne zavolat md_unregister_thread() místo toho, aby to nechal na ovladači konkrétní úrovně RAID. To je zjevné porušení knihovního přístupu. I když to zatím nezpůsobuje žádné problémy, je to přesně ten druh situace, která někdy v budoucnu může vyžadovat přidání zvláštních případů.
Často bylo řečeno, že md a dm by měly být nějakým způsobem sjednoceny (i když méně častěji jsou zvažovány praktické důsledky toho, co to ve skutečnosti znamená). Jak md, tak dm trpí tím, že mají mezivrstvy, což je v důsledku udržuje oddělené. Plné pochopení faktu, že jsou tyto mezivrstvy chyba, a kroky k jejich náhradě efektivní knihovní strukturou pravděpodobně budou důležitým prvním krokem směrem k jakémukoliv sjednocení.
Tímto končí náš průzkum mezivrstev a knihoven v jádře – možná kromě upozornění na nedávné přírůstky, jež zahrnují věci jako libfs, které poskytuje podporu pro virtuální souborové systémy, a libata, které poskytuje podporu pro SATA disky. Ty ukazují, že tendence zbavovat se mezivrstev není jenom přáním autora, ale skutečností v existujícím kódu.
Doufejme, že jsou nyní pochopitelné problémy ohledně vzoru „chyby mezivrstvy“ a výhody následování knihovního přístupu.
Zde také končí tato krátká série o návrhových vzorech v Linuxu. Nepochybně je mnoho dalších, které by bylo možné vypreparovat, pojmenovat a ozřejmit příklady. Ty ale budou muset počkat na jindy.
Jakmile jsou shrnuty, vznikne kolekce, která by měla poskytnout nedocenitelný náhled na to, jak stavět jaderný kód efektivně i jednotně. To následně může být užitečné k pochopení toho, jak funguje současný kód (nebo proč nefunguje), při rozhodování o nových návrzích, ke komentování návrhu během procesu revidování a obecně by to mělo zlepšit viditelnost na této návrhové úrovni tvorby jádra. Doufejme, že to v dlouhodobém měřítku povede ke zvýšení kvality jádra.
Jako příspěvek k tomuto procesu je zde rychle shrnutí Vzorů, které jsme nalezli.
kref: Čítání odkazů, přičemž objekt je zrušen, když je zrušen poslední odkaz na něj.
kcref: Čítání odkazů, přičemž objekt může přetrvat i poté, co je zahozen poslední odkaz na něj.
čisté odkazy: Čítání odkazů, přičemž životnost objektu je podřízena jinému objektu.
posunutý odkaz: Antivzor zahrnující přičítání hodnoty k čítači odkazů, aby se uložil jeden bit informace.
Zabudovaná kotva: Tento vzor je velmi užitečný pro seznamy a lze ho zobecnit, což uvidíte, když prozkoumáte kobjects.
Široká rozhraní: Tento vzor nám připomíná, že snažit se namačkat co nejvíce možných použití do jednoho volání funkce není nutné – prostě poskytněte spoustu volání funkcí (s užitečnými a (snad) konzistentními jmény).
Sada nástrojů: někdy není nejlepší poskytnout pro obecnou službu kompletní řešení, ale místo toho poskytnout sadu nástrojů, které lze využít k vytvoření uživatelského řešení.
Zamykání volajícím: Když máte pochybnosti, vyberte si, aby zámky musel získávat volající raději než volaný. To dává klientu funkce do rukou více kontroly.
Předalokace mimo zámky: V některých ohledech je to vcelku zjevné. V jádře se to nicméně velmi široce používá, takže explicitní pojmenování je dobrý nápad.
Chyba mezivrstvy: Když je potřeba poskytnout několika ovladačům na nižší vrstvě nějakou službu, poskytněte jim ji v knihovně místo toho, abyste jim ji nutili mezivrstvou.
Prozkoumejte rozhraní blkdev_ioctl() v blokové vrstvě, zjistěte, jestli je to spíše knihovna nebo spíše mezivrstva. Porovnejte verze v 2.6.27 a 2.6.28, rozeberte je.
Vyberte si další subsystém jako síťování, vstupní nebo zvuk a prozkoumejte je ve světle tohoto vzoru. Hledejte zvláštní případy a vnucené funkce. Prozkoumejte historii daného subsystému a zjistěte, jestli obsahuje znaky příklonu nebo odklonu od přístupu „mezivrstvy“.
Identifikujte návrhový vzor, který je specifický pro linuxové jádro, ale v této sérii nebyl zmíněn. Pojmenujte a zdokumentujte ho společně s nějakými příklady a protipříklady.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
Odstraňujeme víc svinstva, než přidáváme, mě to přijde jako úspěch!mě → mně
xf86-video-intel
...
Jak je tam napsano, tyka se 950 (Poulsbo, jestli si ten nazev pamatuju dobre - proste ta usporna vec do netbooku). Koupili snad jadro od PowerVR a takhle to dopadlo...
Chráněný RAMFS (PRAMFS)Tak mám zase o práci míň.
chybka v textu: "... požadavky blízku u sebe ..."
… dcache … Není volitelná. Je nucena každému souborovému systému …Bohužel. Třeba Reiser4 s tím má docela problémy, protože některé algoritmy (čti: hacky), které dcache používá, sice krásně fungují pro ext*, XFS apod., ale ne pro FS, který dělá tolik věcí jinak. Nakonec je to vyřešeno tak, že Reiser4 má svůj vlastní kešovací systém (cbk_cache) a dcache dostává třikrát předžvýkané výsledky, se kterými už si dokáže poradit v rozumném čase. Další mezivrstva, tentokrát nutná kvůli výkonu.