Portál AbcLinuxu, 5. května 2025 01:10
Aktuální verze jádra: 2.6.21-rc6. Citát týdne: Jean Delvare. Příliš mnoho vláken. SLUB alokátor.
Překlad reportáží ze stránek LWN.net a KernelTrap.org.
Aktuální předverze řady 2.6 je (k 11. 4. 2007) 2.6.21-rc6, vydaná 5. dubna. Obsahuje slušnou řádku oprav. Linus k tomu řekl: Už bychom se měli blížit k 2.6.21, takže prosím o aktualizaci všech zpráv o regresích.
Od vydání -rc6 bylo do hlavního git repozitáře začleněno několik desítek patchů. Před vydáním 2.6.21 bude pravděpodobně potřeba ještě jedna -rc verze.
Aktuální verze -mm stromu je 2.6.21-rc6-mm1. Mezi nedávné změny patří několik vylepšení podpory notebooků Sony, rozšířená sada háčků paravirt_ops, nové /proc soubory pro zjišťování informací o paměti procesů, přepracování kódu pro zamykání NFS souborů a patche signalfd(). Andrew poznamenal, že -mm je teď poněkud velký patch (25 MB).
Aktuální stabilní jádro řady 2.6 je 2.6.20.6, vydané 6. dubna. 2.6.20.5 bylo vydáno jen o chvilku dříve. Oba patche obsahují dost oprav, včetně jedné v kódu Appletalk, která se týká možnosti vzdáleného shození stroje.
Starší jádra: 2.6.16.47-rc1 vyšlo 11. dubna s přibližně desítkou oprav.
Jestli chceš být správcem subsystému, musíš přispěvatelům do určité míry důvěřovat - a to nejde, pokud jsi perfekcionista. To znamená, že správce by měl(a) být menší perfekcionista než přispěvatelé, jinak bude dělat všechno sám/sama.
-- Jean Delvare
Práce s opravdu velkými stroji je zábavná také proto, že jako první narazíte na nová překvapení v oblasti škálování. Lidé v SGI si tedy často užijí daleko více zábavy než my ostatní. Jejich poslední objev souvisí s počtem jaderných vláken - na systému s 4096 procesory to vede k zajímavým věcem.
Pro začátek přišli na to, že s výchozí konfigurací jádro ani nenabootují. Linuxové systémy mají obyčejně limit 32768 aktivních procesů v určitý okamžik. Pokud už jste někdy spustili ps, určitě jste si všimli, že jaderná vlákna zabírají stále více těchto míst; můj [Jonathan Corbet] jednoprocesorový desktop jich má 39. Jaderných vláken je teď v běžném systému tolik, že na stroji s 4096 procesory zaplní celý vyhrazený prostor - i více. Takový problém se snadno vyřeší zvýšením limitu počtu procesů, ale v tu chvíli to začne být opravdu zajímavé.
Proces init je předkem každého procesu v systému, včetně jaderných vláken. Na velkém systému má tedy init hodně potomků. Tito potomci jsou ve velkém linkovaném seznamu, který musí být prohledáván různými funkcemi, včetně variant funkce wait(). Pokud je hledaný proces ke konci seznamu, může hledání trvat dlouho. A protože 1) většina jaderných vláken žije dlouho a 2) nové procesy se dávají na konec seznamu, je dost pravděpodobné, že hledání se skutečně bude týkat procesu na konci.
Pak, aby to byla opravdu legrace, nahrajte do jádra modul. Proces nahrávání modulu zavolá při linkování nového modulu stop_machine_run(); tato funkce vytvoří pro každý procesor systému jaderné vlákno s vysokou prioritou. Vlákno si zabere přiřazený procesor a bude na něm sedět, dokud mu nebude řečeno, aby ho pustilo; zatímco jsou všechny procesory tímto způsobem uzamčeny, může být provedeno linkování. Volání funkce jako stop_machine_run() je poněkud společensky nevhodné i v ideálních situacích. Ale na systému s 4096 procesory vytvoří stop_machine_run() 4096 procesů, z nichž se každý usadí na konec seznamu potomků init, a každý je nutné vyhledat, když přijde čas se ho zbavit. Výsledkem je systém, který se prostě na delší dobu zastaví.
Dalo by se argumentovat tím, že na tak velkých systémech by se neměly natahovat moduly, ale to by možná uživatelská komunita nepřijala jako vysvětlení. Bylo tedy nutné najít jiná řešení. Zpráva o problému od Robina Holta obsahuje jednoduchý patch, který přesouvá končící procesy na začátek seznamu. Tato změna zařídí, že se při vyhledávání těchto potomků nemusí procházet celým seznamem dlouhotrvajících procesů, které se nikam nechystají.
Linus navrhl pár alternativ. Jedna z nich spočívala ve vytvoření samostatného seznamu pro zombie procesy, což by hledání úplně odstranilo. Druhá by znamenala, že už jaderná vlákna nebudou potomky procesu init, protože mají tak jako tak s uživatelským prostorem málo společného. Ale někteří vývojáři si myslí, že skutečným řešením by bylo začít s omezováním počtů jaderných vláken.
Největším hříšníkem z pohledu vytváření jaderných vláken jsou určitě pracovní fronty, které ve výchozím nastavení vytvářejí jedno vlákno pro každý procesor v systému. Existují situace, ve kterých se více vláken a lokálnost procesorů může hodit, ale bezpochyby jsou i takové, ve kterých všechna ta vlákna potřeba nejsou. Pročištění by pomohlo s některými otázkami škálování a jako dodatečný bonus bychom se zbavili nepořádku ve výpisech ps.
V mnoha případech by pracovní fronta nebyla vůbec nutná. Místo toho by jádro mohlo využít "obecnou" pracovní frontu keventd (která běží jako events/n vláken). Při využití keventd by se mohly vyskytnout problémy s nejistou latencí a teoretickou možností zatuhnutí [deadlock], ale v mnoha situacích by to mohlo fungovat dobře.
V jiných případech však dává smysl vlákno použít. Například úlohy zahrnující dlouhé prodlevy; spouštění funkce s několikavteřinovými pauzami v keventd není považováno za slušné. I práci vyžadující komplikovaný kontext je výhodnější provádět s vlastním vláknem. Často však není nutné vlákna vytvářet, dokud není něco na práci. Na většině systémů ps odhalí vlákna týkající se zpracování chyb, asynchronního I/O, bluetooth apod. V současné době jsou vytvářena při bootu (nebo při natažení modulu) a mnohá až do vypnutí systému nic pořádného nedělají. Vytváření vláken je nenáročné, takže by mohla být vytvářena na vyžádání až ve chvíli, kdy budou potřeba.
V této oblasti by se toho pravděpodobně dalo hodně vylepšovat; je k tomu potřeba, aby se našel někdo, komu na tom záleží, a má čas. Do té doby však vy, kdo máte systémy s 4096 procesory, budete muset používat pár patchů.
Slab alokátor je již řadu let základním stavebním kamenem jaderné správy paměti. Sedí nad nízkoúrovňovým alokátorem stránek a spravuje keše objektů určité velikosti, což umožňuje rychlé a úsporné alokace. Programátoři jádra se v tomto kódu moc nepřehrabují, protože je hodně složitý a většinou funguje docela dobře.
Christoph Lameter je jedním z těch, kterým alokátor zrovna moc dobře nefunguje. Během času sestavil seznam stížností, který už začíná být hodně dlouhý. Alokátor udržuje několik front objektů, které sice alokaci urychlují, ale také věci dost komplikují. Kromě toho s velikostí systému roste i režie způsobená ukládáním:
Fronty objektů SLAB jsou na každém uzlu a procesoru. Fronta alien keše má dokonce pole front, které obsahuje frontu pro každý procesor na každém uzlu. U hodně velkých systémů může počet front a objektů, které se v těch frontách mohou zachytit, růst exponenciálně. Na našich systémech s 1 tisícem uzlů/procesorů máme několik gigabajtů vázaných jen referencemi na objekty těchto front. A to nezahrnuje objekty, které by mohly být ve frontách. Mám obavy, že by těmi frontami jednou mohla být zabrána celá paměť stroje.
Každý slab (skupina jedné nebo více souvislých stránek, ze kterých jsou alokovány objekty) navíc na začátku obsahuje metadata, kvůli kterým je těžké objekty zarovnávat. Kód pro pročišťování keší při nedostatku paměti věci ještě více komplikuje. A tak dále.
Christophovo řešení je SLUB alokátor, kompletní náhrada slab kódu. SLUB slibuje lepší výkon a škálovatelnost díky odstranění většiny front a s tím spojené režie a obecnému zjednodušení slab struktury, přičemž zachovává stávající rozhraní alokátoru.
Ve SLUB alokátoru je slab prostě skupina jedné nebo více stránek úhledně zaplněných objekty dané velikosti. V samotném slabu nejsou žádná metadata - až na to, že volné objekty jsou zformovány do jednoduchého linkovaného seznamu. Když se objeví žádost o alokaci, zjistí se umístění prvního volného objektu a ten je odstraněn ze seznamu a vrácen volajícímu.
Vzhledem k absenci metadat ve slabu bychom se mohli podivovat nad tím, jak se ten první volný objekt hledá. Odpověď je v tom, že SLUB alokátor dává příslušné informace do systémové mapy paměti - struktury page přiřazené ke stránkám tvořícím slab. Zvětšovat struct page se nikdo neodváží, takže SLUB alokátor tuto komplikovanou strukturu ještě více zamotává přidáním dalšího spojení [union]. struct page tedy nakonec dostane tři nová pole, která mají význam pouze v případě, že je přiřazená stránka součástí slabu:
void *freelist; short unsigned int inuse; short unsigned int offset;
freelist ukazuje na první volný objekt v rámci slabu, inuse je počet objektů, které byly ze slabu alokovány, a offset alokátoru říká, kde najít ukazatel na další volný objekt. SLUB alokátor může pro uvolňování objektů použít i RCU, ale aby to bylo možné, tak musí mít možnost dát ukazatel na další objekt mimo samotný objekt; pomocí ukazatele offset alokátor sleduje, kam byl ukazatel umístěn.
Ve chvíli, kdy alokátor vytvoří slab, z něj žádné objekty nebyly alokovány. Jakmile dojde k alokaci, stane se z něj "částečný" slab, který je uložen v seznamu struktury kmem_cache. A protože je to patch zaměřený na škálovatelnost, je jeden seznam "částečných" na každém NUMA uzlu systému. Alokátor se snaží udržovat alokace lokální podle uzlů, ale raději sáhne na další uzly, než aby zaplnil systém částečnými slaby.
Dále je na každém procesoru pole aktivních slabů. Speciální vlákno, které běží prostřednictvím pracovní fronty, monitoruje využití slabů u jednotlivých procesorů; pokud není některých ze slabů na procesoru používán, je zařazen zpátky do seznamu částečných, aby ho mohly využít jiné procesory.
Jakmile jsou všechny objekty ze slabu alokovány, alokátor na slab úplně zapomene. Po uvolnění objektu v plném slabu může alokátor daný slab přemístit přes systémovou mapu paměti a vrátit jej zpátky do příslušného seznamu částečných. Při uvolnění všech objektů v daném slabu (což sleduje čítač inuse) je celý slab vrácen alokátoru k dalšímu využití.
Jednou ze zajímavých funkcí SLUB alokátoru je schopnost kombinovat slaby s podobnými velikostmi a parametry objektů. Výsledkem je méně slab keší v systému (prý až o 50 procent), lepší lokalita slab alokací a menší fragmentace slab paměti. Patch však poznamenává:
Slučování může vyvolat dosud neznámé chyby v jádře, protože poškozené objekty lze teď umístit jinak, takže mohou poškodit odlišné sousední objekty. Pro jejich odhalení zapněte kontrolu normálnosti [sanity checks].
Ačkoliv je obecně vítáno, když něco způsobí odhalení chyb, širší používání SLUB alokátoru by mohlo vést k podivnému chování, dokud tyto chyby nebudou vychytány.
Širší nasazení by mohlo být na pořadu dne: SLUB alokátor je teď v -mm a do hlavního jádra by se mohl dostat už v 2.6.22. Zjednodušený kód je lákavý, stejně jako udávané 5 až 10procentní zvýšení výkonu. Pokud by byl začleněn, nějakou dobu by SLUB alokátor pravděpodobně koexistoval se stávajícím slab alokátorem (a SLOB alokátorem, který je určen pro malé systémy). Při pohledu dále to však vypadá, že se aktuální slab kód blíží konci svého života.
ISSN 1214-1267, (c) 1999-2007 Stickfish s.r.o.