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.
Jádro 2.6.30 je venku, Linus ho vydal 9. června. Některé z větších změn v 2.6.30 zahrnují mnoho vylepšení souborových systémů, patche správy integrity, bezpečnostní modul TOMOYO Linux, podporu protokolu reliable datagram socket, podporu zařízení pro ukládání objektů, cachovací vrstvu souborového systému FS-Cache, souborový systém nilfs, podporu obsluhy přerušení ve vláknech a mnoho dalších. Více informací vizte na stránce o 2.6.30 na KernelNewbies, kompletním changelogu a statistikách v Jaderných novinách pro tento vývojový cyklus.
V době psaní tohoto článku nebyly přijaty žádné patche pro 2.6.31.
V minulém týdnu nebyla vydána žádná stabilní aktualizace; aktualizace 2.6.29.5 je nicméně revidována (společně s aktualizací 2.6.27), vydání se očekává 11. června.
Byla zaslána osmá verze patchů čítačů výkonnosti. Tento kód od svých začátků ušel dlouhou cestu; mezi jinými věcmi je nyní k dispozici nástroj v uživatelském prostoru, který zpřístupňuje veškeré funkce čítačů výkonnosti v jediné aplikaci. Více informací o tom, co se v této oblasti děje, vizte v oznámení.
Patche HPA [host protected area] pro IDE subsystém zde byly zmíněny minulý týden. Od té doby je Bartlomiej Zolnierkiewicz aktualizoval a zaslal jako „opravy IDE“ pro začlenění do jádra 2.6.30. Toto zaslání nicméně vyvolalo nějaké vrásky, protože vydání 2.6.30 bylo v té době na spadnutí. Mnoho vývojářů mělo námitky, že pro přidávání nových vlastností je špatná část vývojového cyklu.
Bartlomiej se však svého požadavku držel s tím, že přidání podpory HPA je skutečně oprava chyby. Dokonce zašel až k náznaku, že skutečný důvod pro opozici k začlenění patchů do 2.6.30 je obava, že by to IDE vrstvě umožnilo překonat kód libata, co se funkčnosti týče. Není potřeba říkat, že nakonec tyto argumenty nepomohly. Kód HPA by měl jít do 2.6.31, ale 2.6.30 bude vydána bez něj.
Padající jádro často vypisuje značné množství užitečných informací. Tyto informace jsou často klíčové k diagnostice a opravě problému. Jsou také často dostatečně obsáhlé, aby nejdůležitější část odrolovala z obrazovky předtím, než si ji kdokoliv stihne přečíst. Když není k dispozici ladící konzole, tato informace se trvale ztrácí – což je přinejmenším otravné.
Tento patch Davea Younga k tomuto problému zaujímá zajímavý přístup. Nový parametr sysfs (/sys/module/printk/parameters/halt_delay) určuje zpoždění v milisekundách; pokud se jádro zastavuje či restartuje, každý řádek vypsaný na konzoli je doprovázen požadovaným zpožděním. S dostatečně vysokým halt_delay by tedy mělo být možné zachytit nejdůležitější informace z výpisu oops předtím, než zmizí. Je to poněkud neotesaný nástroj, ale neotesanost je občas to, co je zapotřebí, když není k dispozici preciznější řešení.
[ping of death]. Dlouho existující chyba v ovladači síťové karty RTL8169 vede k okamžitému pádu, když je přijat paket větší než 1500B, což hlásil Michael Tokarev. Ukázalo se, že karta byla informována o tom, že lze přijímat rámce do velikosti 16 k, ale skb poskytovala přijímací kruhový buffer, který byl jenom 1536 bytů dlouhý. Tato chyba se táhne někam před 2.6.10, tedy do předgitových mlh. Problém byl opraven patchem od Erica Dumazeta, jednalo se o jeden z posledních patchů začleněných před vydáním 2.6.30.
Pro většinu všeobecných nasazení Linuxu se správa napájení skutečně dostává do hry pouze při uspávání a probouzení. Embedded systémy mívají dedikovaný kód, který udržuje spotřebu energie zařízením na minimu, ale k tomu není žádný ekvivalent pro Linux, který lze získat ze standardní instalace distribucí. Nový patch od Rafaela Wysockiho nicméně tuto situaci může změnit a přitom také snížit spotřebu energie a zvýšit spolehlivost správy napájení na obyčejných strojích.
Rafaelův patch přidává infrastrukturu, která podporuje zdánlivě nekontroverzní cíl: Umožnit jádru vypnout zařízení, která se právě nevyužívají. Za tímto účelem získává struct dev_pm_ops dva nové ukazatele na funkce:
int (*autosuspend)(struct device *dev); int (*autoresume)(struct device *dev);
Myšlenka je jednoduchá: Když se jádro rozhodne, že se dané zařízení nepoužívá a lze ho bezpečně vypnout, zavolá se funkce autosuspend(). Někdy v budoucnu je poté volána funkce autoresume(), která zařízení přivede zpátky k životu. Ovladač by samozřejmě měl zajistit všechny potřebné kroky, aby se někde uložil stav zařízení, dokud spí.
Hodnota této infrastruktury by měla být vcelku jasná. Pokud se jádro rozhodne, že fungování systému neutrpí tím, že se dané zařízení vypne, pak může zavolat relevantní funkci autosuspend() a ušetřit nějakou energii. Je zde ale něco navíc: Jádro, které bude používat tuto infrastrukturu, bude pravidelně procvičovat funkce uspávání/probouzení jednotlivých ovladačů. To by mělo odhalit skryté chyby v uspávání/probouzení a zjednodušit tak jejich identifikaci a, doufejme, opravu. Pokud se něco nezdaří, když se uspává/probouzí celý systém, může být těžké najít viníka. Pokud způsobí problémy uspání jediného nečinného zařízení, zdroj problému bude o něco jasnější. Tato infrastruktura by tedy měla vést ke spolehlivější správě napájení obecně, což je dobré.
Pohled na patch ukazuje, že jedna věc chybí: Kód, který rozhoduje o tom, kdy uspat a probudit konkrétní zařízení. Rafael tuto část vynechal jako cvičení pro čtenáře. A jak se ukazuje, různí čtenáři mají různé názory na to, jak k tomuto rozhodnutí dojít.
Matthew Garret rychle naznačil, že by bylo zapotřebí mít mechanismus, kterým by uživatelský prostor mohl nastavit politiky, které mají rozhodovat o uspávání konkrétního zařízení. Podle jeho pohledu prostě není možné, aby jádro samo o sobě vědělo, že je uspání konkrétního zařízení bezpečné. Jeden příklad, který uvedl, je eSATA; rozhraní eSATA není (jádrem) odlišitelné od běžného SATA rozhraní, ale podporuje výměnu disků za chodu. Když jádro uspí „nepoužívaný“ řadič eSATA, nevšimne si, když uživatel připojí nové zařízení. Dalším příkladem jsou klávesnice; může dávat smysl uspat klávesnici, když běží šetřič obrazovky – pokud zrovna uživatel používající přehrávač hudby neočekává, že budou fungovat klávesy ovládající hlasitost. Podobné situace je pro jádro těžké vyřešit.
Ingo Molnár použil jiný přístup; argumentoval, že toto rozhodnutí skutečně přísluší jádru. Jádro by podle něj mělo ve výchozím nastavení uspávat zařízení, kdykoliv je to bezpečné; není tedy důvod se předtím ptát uživatelského prostoru.
Diskuze se táhla bez nějaké shody. Rozdíl mezi těmito postoji nicméně možná nemusí být tak velký, jak se zdá. Ingo je pro to automaticky uspávat zařízení, o kterých se ví, že je to bezpečné, a uznává, že pro většinu zařízení to nemusí platit. To není příliš daleko od Matthewova postoje, že mnoho zařízení není možné bezpečně uspat. Ingo ale říká, že bychom měli pokročit a použít automatickou správu napájení v případech, kde víme, že je to možné, a očekávat, že počet zařízení se sofistikovanou (a správnou) funkcí správy napájení bude růst.
Jediná skutečná neshoda tedy spočívá v tom, jak se má jádro dozvědět, jestli lze dané zařízení uspat, nebo ne. Ingo by byl rád, kdyby na to jádro přišlo z informací, které již má k dispozici; Matthew si myslí, že bude zapotřebí nové rozhraní pro nastavení politik. Řešení této otázky pravděpodobně nebude možné, dokud se někdo nepokusí implementovat politiku řešenou výhradně jádrem, což bude chvíli trvat. Mezitím budeme přinejmenším mít infrastrukturu, která vývojářům umožní začít si hrát s potenciálními řešeními.
Originál tohoto článku pro lwn.net napsal Neil Brown.
Jedním z témat, o která se jaderná komunita zajímá nepřetržitě, je udržování kvality. Je naprosto zjevné, že potřebujeme kvalitu udržovat a dokonce zlepšovat. Méně zjevné je, jak to udělat nejlépe. Jedním širokým přístupem, který se setkal s úspěchem, je zviditelnit různé aspekty jádra. To zviditelňuje i kvalitu těchto aspektů a vede k zlepšení kvality jádra jako celku.
Toto zviditelňování má mnoho podob:
Skript checkpatch.pl ukazuje mnoho odchylek od přijímaného stylu formátování kódu. To povzbuzuje lidi (kteří si vzpomenou, že tento skript mají použít) k tomu, aby tyto chyby ve stylu opravili. Zvýšením viditelnosti příručky stylu zvyšujeme jednotnost vzhledu a tím do určité míry i kvalitu.
Systém „lockdep“ (když je povolen) dynamicky měří závislosti mezi zámky (a s nimi spojenými stavy, jako jestli jsou povolena přerušení). Poté hlásí všechno, co vypadá divně. Tyto podivnosti vždy neznamenají, že je možný deadlock nebo podobný problém, ale v mnoha případech tomu tak je a možnost deadlocku lze odstranit. Zviditelnění grafu závislosti zámků může tedy zvýšit kvalitu.
Jádro obsahuje různé další zlepšení viditelnosti, jako je „otrava“ nevyužívaných částí paměti, takže je neplatný přístup zjevnější, nebo jednoduché použití symbolických jmen místo obyčejných hexadecimálních adres ve výpisu zásobníku, takže jsou hlášení o chybách užitečnější.
Na mnohem vyšší úrovni software pro sledování revizí „git“, který se používá pro sledování změn v jádře, umožňuje snadno zjistit, kdo co udělal a kdy. Fakt, že ke každému patchi umožňuje přidat komentář, zjednodušuje odpověď na otázku „Proč vypadá tento kód takto?“ Toto zviditelnění může zlepšit chápání kódu a tím pravděpodobně vede ke zlepšení kvality tím, že jsou vývojáři informovanější.
Je mnoho dalších oblastí, kde může větší zviditelnění zlepšit nebo už zlepšuje kvalitu. V tomto seriálu se budeme zabývat jednou oblastí, o které si autor myslí, že by mohla být viditelnější takovým způsobem, který by snadno mohl vést ke zlepšení kvality. Touto oblastí je pojmenování návrhových vzorů specifických pro jádro.
„Návrhový vzor“ je koncept, který byl poprvé zaveden na poli architektury a do výpočetní techniky, konkrétně na pole objektově orientovaného programování, se dostal publikací Návrhové vzory: Prvky opakovaně využitelného objektově orientovaného softwaru, která byla vydána roku 1994. Wikipedia má o tomto tématu další užitečné informace.
Návrhový vzor ve stručnosti popisuje konkrétní třídu návrhového problému a rozvádí přístup, který se v minulosti při řešení tohoto problému ukázal jako efektivní. Jedním konkrétním přínosem návrhového vzoru je to, že kombinuje popis problému a popis řešení zároveň a pojmenovává je. Mít jednoduché a snadno zapamatovatelné jméno pro vzor je velmi užitečné – pokud jak vývojář, tak revidovatel znají pro stejné vzory stejná jména, pak lze významné rozhodnutí o návrhu sdělit v jednom nebo dvou slovech, díky čemuž je toto rozhodnutí viditelnější.
V linuxové kódové základně je mnoho návrhových vzorů, které se ukázaly být efektivní. Většina z nich však nikdy nebyla zdokumentována, takže nejsou snadno dostupné dalším vývojářům. Autor článku doufá, že explicitní dokumentací těchto vzorů může přispět k jejich širšímu využívání a tím k tomu, že budou vývojáři schopni dosáhnout efektivních řešení běžných problémů rychleji.
Ve zbytku tohoto seriálu se budeme dívat na tři oblasti problémů a hledání různých návrhových vzorů. Jejich významnost a záběr se značně různí. Naším cílem nebude jenom pojmenovat tyto vzory, ale také ukázat jejich hodnotu a rozsah, aby se jiní mohli snažit pojmenovat vzory, které již viděli.
Bude ukázáno na mnoho příkladů z linuxového jádra, protože příklady jsou důležitou částí osvětlení každého vzoru. Pokud nebude řečeno jinak, tyto příklady pochází z 2.6.30-rc4.
Nápad počítat odkazy a tím řídit životnost objektu je poměrně běžný. Základem tohoto nápadu je mít čítač, který se inkrementuje, když vznikne nový odkaz, a dekrementuje, když se odkaz přestane používat. Když čítač dosáhne nuly, všechny zdroje využívané daným objektem (jako například paměť, ve které je uložen), lze uvolnit.
Mechanismus pro správu čítání odkazů je poměrně jednoduchý. Jsou zde nicméně záludnosti, které značně zjednodušují možnost použít tento mechanismus špatně. Částečně z tohoto důvodu má (od roku 2004) linuxové jádro datový typ známý jako „kref“ spojený s podpůrnými funkcemi (vizte Documentation/kref.txt, <linux/kref.h> a lib/kref.c). Ty obalují některé z těchto záludností a konkrétně jasně říkají, že se daný čítač konkrétním způsobem využívá jako čítač referencí. Jak bylo zmíněno výše, jména návrhových vzorů jsou velmi užitečná a jenom poskytnutí jména, které mohou jaderní vývojáři použít, je pro revidovatele velmi užitečné.
Co se podpory návrhových vzorů týče, začlenění kref do linuxového jádra má plus i minus. Plus je vyslouženo tím, že kref zjevně obsahuje důležitý návrhový vzor, je dobře zdokumentováno a v kódu je jasně vidět, když se použije. Mínus si nicméně zaslouží, protože obaluje jenom část počítání odkazů. Jak za chvíli uvidíme, existují některá použití čítání odkazů, která do modelu kref nezapadají dobře. Mít „požehnaný“ mechanismus pro čítání odkazů, který neposkytuje potřebnou funkčnost, může ve skutečnosti zapříčinit chyby, když se lidé pokusí kref vložit tam, kam nepatří, a budou si myslet, že bude prostě fungovat, i když ve skutečnosti to tak není.
Užitečným krokem směrem k pochopení složitostí čítání odkazů je pochopit, že k objektu často existují dva oddělené druhy odkazů. V pravdě mohou být tři nebo ještě více, ale to je velmi výjimečné a obvykle to lze zobecnit případem se dvěma. Tyto dva typy odkazů budeme nazývat „externí“ a „interní“, i když v některých případech by bylo vhodnější „silné“ a „slabé“.
„Externí“ odkaz je takový druh odkazu, na který jsme pravděpodobně nejvíce zvyklí. Je čítán pomoci „get“ a „put“ a mohou ho držet subsystémy poměrně vzdálené od subsystému, který objekt spravuje. Existence čítaného externího odkazu má silný a jednoduchý význam: Tento objekt se používá.
Oproti tomu „interní“ odkazy se často nečítají a jsou drženy pouze interně systémem, který objekt spravuje (nebo jeho blízkým příbuzným). Různé interní odkazy mohou mít velice se různící významy a tedy i velice se různící implikace pro implementaci.
Pravděpodobně nejznámější příklad interních odkazů je cache, která poskytuje službu „vyhledávání podle jména“. Když znáte jméno objektu, můžete požádat cache o externí odkaz – za předpokladu, že objekt v cache skutečně existuje. Taková cache by potom měla objekt v seznamu nebo v jednom z několika seznamů například pod hash tabulkou. Přítomnost objektu na takovém seznamu je odkaz na objekt. Není to nicméně čítaný odkaz, neznamená „tento objekt se používá“, ale pouze „tento objekt se tu někde povaluje, kdyby ho náhodou někdo chtěl“. Objekty nejsou ze seznamu vyjímány, dokud nejsou zahozeny všechny externí odkazy, přičemž je možné, že nejsou vyjmuty ani hned poté, co se tak stane. Existence a povaha interních odkazů má zjevně významné dopady na to, jak je čítání odkazů implementováno.
Jedním z užitečných způsobů, jak klasifikovat styly čítání referencí, je podle potřebné implementace operace „put“ (odlož). Operace „get“ (získej) je vždycky stejná, vezme externí odkaz a vytvoří další externí odkaz. Je implementována nějak takto:
předpokládejme(objekt->počet_odkazů > 0); inkrementuj(objekt->počet_odkazů);
Nebo v C v linuxovém jádře:
BUG_ON(atomic_read(&obj->refcnt)) ; atomic_inc(&obj->refcnt);
Všimněte si, že „get“ nelze použít na objekt, na který není odkazováno. Je zde zapotřebí něco jiného.
Operace „put“ existuje ve třech variacích. I když je mezi jednotlivými případy nějaký překryv, je dobré mít je oddělené, aby se udržela čistota kódu. Tyto tři možnosti v jazyce C jsou:
1 atomic_dec(&obj->refcnt); 2 if (atomic_dec_and_test(&obj->refcnt)) { … něco udělej … } 3 if (atomic_dec_and_lock(&obj->refcnt, &subsystem_lock)) { ..… něco udělej …. spin_unlock(&subsystem_lock); }
Začneme volbou číslo „2“, což je styl využitý pro kref. Tento styl je vhodný, když objekt nepřežije svůj poslední externí odkaz. Když se počet odkazů sníží na nulu, objekt je potřeba uvolnit nebo s ním něco udělat, takže je nutné testovat nulovost pomocí atomic_dec_and_test().
Objekt, který zapadá do tohoto stylu, často nemá žádné interní odkazy, kterými by se bylo potřeba zabývat, což je případ většiny objektů v sysfs, přičemž sysfs je významný uživatel kref. Pokud ale objekt, používající styl kref, má interní odkazy, nelze mu umožnit vytvořit externí odkaz z interního odkazu, pokud se neví, že existují další externí odkazy. Pokud je to zapotřebí, je k dispozici primitivum
atomic_inc_not_zero(&obj->refcnt);
které inkrementuje hodnotu za předpokladu, že není nulová, a vrací výsledek, který informuje o úspěšnosti. atomic_inc_not_zero() je v jádře relativně nový vynález, který se objevil koncem roku 2005 jako součást práce na bezzámkové cache. Z tohoto důvodu není široce využíván a hodně kódu, který by z něj mohlo těžit, místo toho používá spinlocky. Smutné je, že balíček kref toto primitivum také nepoužívá.
Zajímavý příklad tohoto stylu odkazu, který nepoužívá kref a dokonce nepoužívá ani atomic_dec_and_test() (i když by mohl a pravděpodobně i měl) jsou dva čítače odkazů ve struct super: s_count a s_active.
s_active zapadá do stylu čítání odkazů kref přesně. Superblock svůj život začíná s hodnotou s_active 1 (nastavenou v alloc_super()) a když s_active dosáhne nuly, nelze pořídit další externí odkazy. Toto pravidlo je zakódováno v grab_super(), i když to není zcela zřejmé. Současný kód (z historických důvodů) přidává k s_count velmi vysokou hodnotu (S_BIAS) v případě, že je s_active nenulové, a grab_super() místo testu, jestli je s_active nulové, testuje, jestli je s_count větší než S_BIAS. Stejně tak jednoduše by ten druhý test bylo možné provést pomocí atomic_inc_not_zero() a tím se vyhnout použití spinlocků.
s_count poskytuje jiný druh odkazu, který má jak „interní“, tak „externí“ aspekty. Je interní v tom, že je jeho sémantika mnohem slabší než ta u odkazů čítaných pomocí s_active. Odkazy čítané pomocí s_count znamenají pouze „tento superblok není možné uvolnit právě teď“ bez předpokladu, že je skutečně aktivní. Externí je v tom, že se podobá kref, svůj život začíná nastavený na 1 (dobře, ve skutečnosti na 1*S_BIAS) a když dosáhne nuly (v __put_super()), je superblok zrušen.
Jsou tedy dva čítače odkazů, které by bylo možné nahradit dvěma kref za předpokladu, že:
čímž by mohlo zmizet několik volání spinlocků. Detaily ponecháváme jako cvičení pro čtenáře.
Linuxové jádro nemá objekt „kcref“, ale toto jméno se zdá vhodné pro další styl čítání odkazů; „c“ zde značí „cachovaný“, protože je to styl, který se často využívá v cachích. Je to tedy v jádře cachovaný odkaz [Kernel Cached REFerence].
kcref používá atomic_dec_and_lock(), jak je to dáno v možnosti tři výše. To proto, že při odložení posledního odkazu musí být uvolněn nebo otestován, jestli není zapotřebí nějaké další zpracování. To se musí udělat pod zámkem, aby se zajistilo, že nevzniknou další odkazy, zatímco je stav vyhodnocován.
Jednoduchým příkladem je zde čítač odkazů i_count ve struct inode. Důležitá část iput() je:
if (atomic_dec_and_lock(&inode->i_count, &inode_lock)) iput_final(inode);
kde iput_final() zkoumá stav inodu a zjišťuje, jestli je možné ho zrušit, nebo jestli má být ponechán v cache pro případ, že by mohl být brzy znovu použit.
Mezi jinými věcmi inode_lock brání vytvoření nových externích odkazů z interních odkazů z hash tabulky inodů. Z tohoto důvodu je konverze interních odkazů na externí povolena jenom v případě, že je držen inode_lock. Není náhoda, že funkce, která to podporuje, se nazývá iget_locked() (či iget5_locked()).
Trochu složitější příklad je struct dentry, kde je d_count spravován jako kcref. Je složitější, protože je potřeba získat dva zámky předtím, než si můžeme být jisti, že nelze vytvořit nový odkaz – jak dcache_lock, tak de->d_lock. To vyžaduje, abychom buď drželi jeden zámek a pak atomic_dec_and_lock() druhý (jako je to v prune_one_dentry()), nebo nejprve atomic_dec_and_lock() první, poté získat druhý a znovu počet odkazů otestovat – jako je to v dput(). To je dobrý příklad faktu, že nikdy nelze předpokládat, že jsme obalili všechny možné styly čítání odkazů. Těžko šlo předpokládat, že budou zapotřebí dva zámky.
Ještě složitější čítač odkazů ve stylu kcref je mnt_count ve struct vfsmount. Složitost je zde dána tím, že struktura má dva čítače odkazů: mnt_count, což je poměrně přímočarý počet externích odkazů, a mnt_pinned, který počítá interní odkazy z modulu účtování procesů. Konkrétně počítá počet účetních souborů, které jsou na souborovém systému otevřeny (a jako takový by mohl mít smysluplnější jméno). Složitost zde spočívá v tom, že pokud zbývají pouze interní odkazy, jsou všechny konvertovány na externí. Objevování detailů je zde opět ponecháno čtenáři.
Poslední styl čítání odkazů obsahuje pouze snížení počtu odkazů (atomic_dec()) s tím, že nic víc se nedělá. Tento styl je v jádře relativně vzácný, a to z dobrého důvodu. Ponechávat objekty, na které se nikdo neodkazuje, jen tak se povalovat kolem, není dobrý nápad.
Jedno použití tohoto stylu je v struct buffer_head, jež je spravována v fs/buffer.c a <linux/buffer_head.h>. Funkce put_bh() je prostě:
static inline void put_bh(struct buffer_head *bh) { smp_mb__before_atomic_dec(); atomic_dec(&bh->b_count); }
To je v pořádku, protože pravidla týkající se životnosti buffer_head jsou úzce spojena se stránkou. Ve stránce je alokována jedna nebo více struktur buffer_head, když je potřeba ji rozsekat na menší kousky (buffery). Buffery mají tendence ve stránce zůstat do doby, než je uvolněna, v tom okamžiku je buffer_head vymazána (funkcí drop_buffers() volanou z try_to_free_buffers()).
Obecně je styl „plain“ (prostý) vhodný, pokud se ví, že se objekt neztratí, protože na něj bude vždy existovat nějaký interní odkaz, a že vždy bude nějaký proces, který tento interní odkaz využije, aby objekt nalezl a uvolnil.
Tento malý přehled čítání odkazů jako úvodu do návrhových vzorů zakončíme diskuzí o konceptu antivzoru. Zatímco vzory jsou přístupy, o kterých se prokázalo, že fungují a je potřeba je podporovat, antivzory jsou přístupy, u kterých se prokázalo, že nefungují dobře a je vhodné se jim vyhýbat.
Autor článku by rád naznačil, že využívání „posunu“ [bias] v referenčním odkazu je příklad antivzoru. Posun je v tomto kontextu velká hodnota, která je přičtena k/odečtena od počtu odkazů a v podstatě se používá k tomu, aby se uložil jeden bit informace. Na posun jsme již narazili u správy superbloků s_count. V tomto případě hodnota posunu oznamuje, že je hodnota s_active nenulová, což je dostatečně snadné přímo testovat. Posun zde tedy není užitečný a jenom zamlžuje pravý účel kódu.
Dalším příkladem posunu je správa struct sysfs_dirent v fs/sysfs/sysfs.h a fs/sysfs/dir.c. Zajímavé je, že sysfs_dirent má dva čítače odkazů stejně jako superbloky, které jsou také nazvány s_count a s_active. V tomto případě má s_active velký záporný posun v případě, že je záznam právě deaktivován. Stejnou informaci by nicméně bylo možné stejně efektivně a možná mnohem zjevněji uložit do slova příznaků s_flags. Ukládání jednobitových informací do příznaků je mnohem snáze pochopitelnější než jejich ukládání do čítače a mělo by tedy být preferováno.
Obecně používání posunu nezvyšuje čitelnost kódu a není to běžný vzor. Nemůže přidat více funkčnosti, než by mohl poskytnout jednobitový příznak, a bylo by extrémně ojedinělé, kdyby panoval takový nedostatek paměti, že by se nenašel jediný volný bit, do kterého by se uložilo to, co je jinak indikováno přítomností posunu. Z těchto důvodů by se posuny měly považovat za antivzory a pokud možno by se neměly vůbec používat.
Tím se uzavírá naše zkoumání různých návrhových vzorů týkajících se čítání odkazů. Jenom mít terminologii jako „kref“ versus „kcref“ a „externí“ vs „interní“ odkazy může být velmi užitečné při zviditelňování chování různých odkazů a čítačů. Mít kód, který to zajišťuje, stejně jako to děláme s kref a mohli bychom s kcref, a používat tento kód při každé příležitosti by bylo velkou pomocí jak pro vývojáře, kterým by se zjednodušilo nalezení správného modelu napoprvé, tak pro revidovatele, kteří by jasněji viděli, co je záměr.
Návrhové vzory, které jsme pokryli v tomto článku, jsou:
kref: Pokud životnost objektu trvá jenom do doby, než je zahozen poslední odkaz, je vhodný kref. Pokud na objekt existují nějaké interní reference, je možné povýšit je na externí pouze atomic_inc_not_zero(). Příklady: s_active a s_count ve struct super_block.
kcref: Může-li životnost objektu přesáhnout zahození posledního odkazu, je vhodný kcref se svým atomic_dec_and_lock(). Jakýkoliv interní odkaz lze na externí konvertovat pouze v případě, kdy je držen subsystémový zámek. Příklady: i_count ve struct inode.
plain: Pokud je životnost objektu podřízena životnosti jiného objektu, je vhodný návrhový vzor plain. Nenulové počty odkazů na objekt je nutné považovat za interní odkazy na rodičovský objekt a konverze interních odkazů na externí musí dodržovat stejná pravidla jako pro rodičovský objekt. Příklady: b_count ve struct buffer_head.
posunutý odkaz: Máte-li pocit, že je potřeba k hodnotě počtu odkazů přidat velký posun, abyste indikovali nějaký konkrétní stav, nedělejte to. Použijte příznakový bit někde jinde. Toto je antivzor.
Příští týden se podíváme na další oblast, kde se v Linuxu prokázaly nějaké úspěšné návrhové vzory, a prozkoumáme o něco bohatší téma složitých datových struktur.
Jak bylo při přípravě tohoto seriálu autorovi připomenuto, když jde o zjednodušení pochopení kódu, není nad jeho cílené studium. Vzhledem k tomu je zde pro čtenáře, který má zájem, několik cvičení.
Nahraďte s_active a s_count ve struct super krefy, přitom se zbavte S_BIAS. Porovnejte výsledek s originálem použitím trojice Správnost, Udržovatelnost a Výkonnost.
Zvolte smysluplnější jméno pro mnt_pinned a s tím spojené manipulační funkce.
Přidejte do knihovny kref funkci, která využívá atomic_inc_not_zero(), a jejím použitím (nebo naopak) odstraňte atomic_dec_and_lock() v kref v net/sunrpc/svcauth.c – toto použití narušuje abstrakci kref.
Prozkoumejte čítač odkazů _count ve struct page (vizte příklad v mm_types.h) a rozhodněte, jestli se chová jako kref nebo jako kcref (nápověda: není to „plain“). To by mělo zahrnovat identifikaci všech interních odkazů a s nimi spojených pravidel zamykání. Určete, proč cache stránek (struct address_space.page_tree) vlastní čítaný odkaz, nebo vysvětlete, proč by neměla. To bude zahrnovat pochopení page_freeze_refs() a jeho použití v __remove_mapping(), stejně jako page_cache_{get,add}_speculative().
Kredity navíc: Poskytněte sérii minimálních uzavřených patchů, které implementují změny vzešlé ze zkoumání uvedených výše.
Nástroje: Tisk bez diskuse
Tiskni
Sdílej:
Jaderné noviny - 10.6. 2009 vyšly dnes 7.7. 2009??
Ano - prekukol si to, si neprekonatelny genius. V piatok alebo v stredu ta navrhnu na nobelovu cenu.
Myslím, že trochu přeháníš. Aby Ti nepraskla pumpa.