Portál AbcLinuxu, 6. května 2025 09:32

Jaderné noviny - 20. 11. 2007

19. 12. 2007 | Robert Krátký
Články - Jaderné noviny - 20. 11. 2007  

Aktuální verze jádra: 2.6.24-rc3. Citáty týdne: Greg Kroah-Hartman, Linus Torvalds. Systémové volání sys_indirect() pro nepřímé volání systémových volání. Jmenné prostory PID v jádře 2.6.24: API pro uživatelský prostor, interní API.

Obsah

Aktuální verze jádra: 2.6.24-rc3

link

Aktuální předverze je (20. 11. 2007) 2.6.24-rc3, vydaná 16. listopadu. Kromě spousty oprav obsahuje podporu nových zařízení I/OAT a patch označující funkci jmenných prostorů PID jako "experimentální". Vizte krátký changelog se seznamem patchů nebo dlouhý se všemi podrobnostmi.

Od vydání -rc3 bylo zatím do hlavního git repozitáře začleněno jen několik oprav.

Aktuální vydání stabilní řady 2.6 je 2.6.23.8 ze 16. listopadu. Pár dní předtím Greg Kroah-Hartman poznamenal:

Ok, trochu jsem se v poslední době se -stable loudal a neuvědomil jsem si, jak moc pozadu už jsem. Všichni posílali patche, což je skvělé, ale máme teď před sebou obrovské vydání, kde by jich bylo 114.

Aby usnadnil kontrolu kódu, rozdělil patche do několika částí, z nichž každá vyšla jako samostatná stabilní verze. Takže máme:

2.6.23.9, které obsahuje 29 patchů, je teď připravováno.

Starší jádra: 2.6.22.13 vyšlo také 16. listopadu a obsahovalo pouze bezpečnostní opravy. 2.6.22.14, ve kterém je dalších pár desítek oprav, se teď připravuje.

Ještě starší jádra: 2.4.35.4 vyšlo 17. listopadu s několika opravami. 2.4.36-pre2 vyšlo s převážně těmi samými opravami.

Citáty týdne: Greg Kroah-Hartman, Linus Torvalds

link

A co se týče 802.11n, tak už jsem _takhle_ blízko k získání specifikací důležitého čipsetu, takže snad pro vás budeme mít zanedlouho práci. Teď kdyby si jen pospíšili ti právníci...

>-- Greg Kroah-Hartman stupňuje napětí.

Vždycky jsem si o HIGHMEM myslel, že je to prostě nepoužitelné. Je to OK pro rozšíření 2 - 4 GB (tj. HIGHMEM4G, ne 64G) a asi je to i na hraně použitelnosti při 4 -8 G, když si dáváte pozor.

Ale upřímně řečeno se odmítám starat o to, co se děje nad to. Pokud máte ve svém stroji 12 G (nebo, bůh chraň, ještě více) a neráčíte se obtěžovat upgradovat na 64bitový procesor, tak *mně* nestojí za to si kvůli tomu dělat vrásky.

-- Linus Torvalds

Systémové volání sys_indirect() pro nepřímé volání systémových volání

link

Příprava uživatelských API je těžká. I když se rozhraní v době vytvoření zdá kompletní a dobře navržené, v budoucnu se často objeví nové požadavky, které může staré API jen těžko uspokojit. Unix například začal se systémovým voláním wait(). Přišly však komplexnější aplikace a začalo být potřeba čekat na konkrétní procesy, získat o končícím procesu více informací, čekat tak, aby nedošlo k bloku, atd. Takže kromě wait() teď máme ještě waitid(), waitpid() a wait4(). Protože staré verze systémových volání nemohou být (téměř) nikdy odstraněny, proměnlivé potřeby mají tendenci časem způsobovat přibývání nových volání.

Nedávno Ulrich Drepper požadoval, aby bylo možné přidávat příznaky k systémovým voláním, která vytvářejí popisovače souborů, ale nemají parametr pro příznaky. Příkladem mohou být třeba socket() a accept(). Chování popisovačů souborů, které byly vytvořeny pomocí těchto systémových volání, je možné upravit zpětně (pomocí fcntl()), ale vždycky tam bude chvilka, během které bude popisovač existovat, aniž by bylo nastaveno požadované chování. Jedná-li se například o "close on exec" [zavřít při spuštění] a zároveň běží vícevláknový program, mohlo by jedno vlákno spustit nový program pomocí exec() ještě předtím, než by jiné stihlo nastavit příznak "close on exec". Výsledkem takového souběhu by byl děravý popisovač souborů, který by mohl znamenat bezpečnostní problém. Jediný způsob, jak tomuto konkrétnímu souběhu zabránit, je přinutit jádro, aby popisovače souborů vytvářelo už s nastavenými požadovanými příznaky.

Tradičně by tento problém řešilo přidání nového systémového volání; mohlo by se například přidat 4parametrové volání socket4(), které by mělo požadovaný parametr flags. Takové řešení však není optimální; jak bylo řečeno, vede to k neustále narůstajícímu počtu systémových volání. Bylo by tedy fajn najít jiné řešení. Ulrich si myslí, že se mu povedlo právě to, když přidal jediné systémové volání (indirect()), které funguje tak, že přidává dodatečné parametry k existujícím voláním.

První implementaci sys_indirect() vytvořil Davide Libenzi v červenci. S tím kódem však Ulrich nebyl moc spokojen:

Davideova předchozí implementace je podle mého názoru mnohem komplikovanější, než by bylo potřeba. Tento kód je, jak se můžete přesvědčit, triviální. Minulý týden jsem o tomto přístupu mluvil s Linusem a na chvilku jsme spolu dokonce v něčem souhlasili.

Prototyp nového systémového volání vypadá takto:

    int indirect(struct indirect_registers *regs,
                 void *userparams,
		 size_t paramslen,
		 int flags);

Struktura regs obsahuje registry procesu, které se obyčejně používají v systémových voláních; jinými slovy číslo systémového volání a jeho (normální) parametry. Dodatečné parametry, které mají být systémovému volání předány, jsou v userparams a jejich délka je paramslen. Parametr flags není v současné době využit; je tam kvůli případnému budoucímu rozšíření, protože není možné rozšířit indirect() sebou samým.

Struktura task_struct byla rozšířena o nové pole:

    union indirect_params indirect_params;

Tato union má obsahovat pole pro každý druh parametru, který by mohl být k systémovému volání přidáván; v Ulrichově kódu vypadá takto:

    union indirect_params {
	struct {
	    int flags;
	} file_flags;
    };

Může být tedy použita k předání parametru flags systémovým voláním, které se zabývají popisovači souborů.

Když je zavoláno indirect(), zkontroluje si, jestli je číslo požadovaného systémového volání na interním seznamu povolených. Pokud nebylo dané systémové volání označeno jako rozšiřitelné tímto způsobem, volání selže s EINVAL. V opačném případě jsou parametry překopírovány do struktury task_struct aktuálního procesu a volání je spuštěno běžným způsobem. Po dokončení volání je oblast indirect_params ve struktuře úlohy vynulována.

Jádro systémovému volání nedává nijak najevo, že bylo spuštěno prostřednictvím indirect(); jediným rozdílem v takovém případě je, že v indirect_params mohou být nenulové hodnoty. Takže mechanismus lze také chápat jako způsob, jak k systémovým voláním přidat parametry s výchozí hodnotou nula. Bez dalších úprav tedy není možné přidat systémovému volání parametr, u kterého by předání nuly mělo jiný význam než úplné vynechání parametru.

Pokud by se v budoucnu objevila potřeba přidat další parametr, může být velikost struktury indirect_params zvětšena. Za předpokladu, že si jádro udrží staré chování, když má nový parametr hodnotu nula, starší aplikace a knihovny budou fungovat jako dříve. Rozšířené systémové volání nepotřebuje (a nemůže) vědět, jestli je zvětšená struktura indirect_params používána, nebo ne.

Nabízí se také možnost využití tohoto mechanismu nad rámec rozšiřování systémových volání: vývojáři sysletů v tom vidí způsob, jak specifikovat asynchronní chování. Aktuální patche implementující syslety jsou v podstatě jen obalová vrstva kolem systémových voláních, která říká, že jde o asynchronní volání (a co dělat s výsledky). Přidávat systémovým voláním dvě nepřímé vrstvy se nezdá jako optimální řešení, takže někteří vývojáři projevili zájem přidat místo toho informace o sysletech do indirect(). To je jeden ze zamýšlených účelů zatím nepoužívaného parametru flags.

Samozřejmě se nečeká, že by aplikace volaly indirect() přímo. Daleko pravděpodobnější se zdá, že by indirect() bylo zavrtáno hluboko v C knihovně, která by pro aplikace exportovala přímočařejší rozhraní.

Zatímco některým vývojářům (včetně Linuse, zjevně) se patch líbí, jiní moc nadšení nejsou. David Miller si nebral servítky: Řekl bych, že tohle nepřímé systémové volání je nejošklivější rozhraní, které jsem kdy viděl navrhované pro jádro. Na H. Peter Anvina to také dojem neudělalo:

Myslím, že je to děsná berlička. Zase další multiplexor, kterým se v jádře tolik snažíme vyhýbat. A aby to bylo ještě horší, tak je to multiplexor, který vytváří zase další jednoúčelovou konvenci pro volání, zatímco bychom se měli snažit jaderné volací konvence co nejvíce sjednotit.

Nebylo by tedy nijak překvapivé, kdyby se před začleněním do jádra toto rozhraní ještě vyvíjelo - je to nové a trochu zrádné API, kterému by diskuze jen prospěla. Protože však za touto prací stojí reálná potřeba, je pravděpodobné, že se indirect() nakonec v nějaké podobě v hlavním jádře objeví.

Jmenné prostory PID v jádře 2.6.24

link

Autoři: Pavel Emeljanov a Kir Kolyškin [Pavel Emelyanov a Kir Kolyshkin].

Jednou z nových vlastností v jádře 2.6.24 je podpora jmenných prostorů PID, kterou vyvinul tým OpenVZ s pomocí IBM. Jmenný prostor PID umožňuje vytvářet sady úloh, z nichž každá vypadá - z pohledu ID procesů - jako samostatný systém. Jinými slovy, v různých jmenných prostorech můžete mít stejná ID.

Tato funkce je základním předpokladem pro migraci kontejnerů mezi hostiteli; když máme jmenný prostor, můžeme jej přesunout na jiného hostitele, ale ponechat hodnoty PID -- a to je nutné, protože úloze by se nemělo měnit PID. Bez této funkce by migrace s největší pravděpodobností selhala, jelikož na cílovém uzlu by mohly existovat procesy se stejnými ID, což by způsobilo konflikty při adresování úloh prostřednictvím jejich ID.

Jmenné prostory PID jsou hierarchické; při vytvoření nového jmenného prostoru PID uvidí všechny procesy v aktuálním jmenném prostoru úlohy v tom novém (tj. budou schopné je adresovat pomocí PID). Nicméně, úlohy v novém jmenném prostoru neuvidí ty v aktuálním. To znamená, že od té chvíle má každý proces více než jedno PID -- jedno pro každý jmenný prostor.

API pro uživatelský prostor

link

Nový jmenný prostor lze vytvořit zavoláním systémového volání clone(2) s nastaveným příznakem CLONE_NEWPID. Pak bývá vhodné změnit kořenový adresář a připojit novou instanci /proc, aby fungovaly běžné utility, např. ps. Vzhledem k tomu, že předek zná PID svého potomka, je pro čekání na ukončení možné použít běžným způsobem wait().

První úloha v novém jmenném prostoru bude mít PID 1. Bude to tedy jeho init a všechny osiřelé úlohy se stanou jejími potomky. Na rozdíl od samostatných strojů může tento init skončit, což znamená ukončení celého jmenného prostoru.

Protože teď máme izolované skupiny úloh, měli bychom zařídit, aby proc ukazoval pouze ty PID, které jsou pro danou úlohu viditelné. Abychom toho dosáhli, musí být procfs připojen vícekrát -- jednou pro každý jmenný prostor. Pak budou PID zobrazené v připojené instanci právě z toho jmenného prostoru, kde proběhlo připojení.

Například pokud uživatel vytvoří nový adresář proc_2, založí nový jmenný prostor PID a připojí tam procfs, uvidí PID, jak vypadají v rámci nového jmenného prostoru. Bude tam PID číslo 1, což je init daného jmenného prostoru, a všechny další PID, které mohou být stejné jako PID v aktuálním jmenném prostoru, ale přitom mohou odkazovat na jinou úlohu.

Žádné další změny v uživatelském API nejsou potřeba. Úlohy mohou i nadále získávat své PID, PGID atd. s pomocí známých systémových volání. Mohou také pracovat se sezeními a skupinami. Úlohy mohou vytvářet vlákna a pracovat s futexy.

Interní API

link

Všechna PID, které může úloha mít, jsou popsána v struct pid. Tato struktura obsahuje hodnotu ID, seznam úloh, které mají toto ID, referenční čítač a hashovanou položku seznamu, která je uložena v hashované tabulce pro rychlejší vyhledávání.

Pár slov o seznamech úloh. Každá úloha má v podstatě tři PID: ID procesu (PID), ID skupiny procesů (PGID) a ID sezení (SID). PGID a SID mohou být úlohami sdílené. Pokud například dvě nebo více úloh náleží do stejné skupiny, pak ID skupiny odkazuje na více než jednu úlohu.

Se jmennými prostory PID je tato struktura pružná. Každé PID může mít několik hodnot, přičemž každá bude platit v jiném jmenném prostoru. Takže úloha může mít PID 1024 v jednom jmenném prostoru a 256 v jiném. Struktura struct pid je tedy změněna.

Takhle vypadala struct pid před zavedením jmenných prostorů PID:

    struct pid {
	atomic_t count;				/* referenční čítač */
	int nr;					/* hodnota pid */
	struct hlist_node pid_chain;		/* hash */
	struct hlist_head tasks[PIDTYPE_MAX];	/* seznam úloh */
	struct rcu_head rcu;			/* RCU pomocník */
    };

A takhle vypadá teď:

    struct upid {
	int nr;					/* přesunuto ze struct pid */
	struct pid_namespace *ns;		/* jmenný prostor, ve kterém
						   je tato hodnota vidět */
	struct hlist_node pid_chain;		/* přesunuto ze struct pid */
    };

    struct pid {
	atomic_t count;
	struct hlist_head tasks[PIDTYPE_MAX];
	struct rcu_head rcu;
	int level;				/* počet upid */
	struct upid numbers[0];
    };

Jak vidíte, struct upid teď reprezentuje hodnotu PID. Pro převod struct pid na PID a naopak můžete použít sadu pomocníků, např. task_pid_nr(), pid_nr_ns(), find_task_by_vpid() atd.

Všechna tato volání mají v názvech informaci:

..._nr()
Tato volání pracují s tzv. "globálními" PID. Globální PID jsou čísla, která jsou v celém systému unikátní, stejně jako byly dříve PID. Např. pid_nr(pid) vám řekne globální PID dané struct pid. Hodí se pouze v případě, že hodnota PID neopustí jádro. Například pokud nějaký kód potřebuje uložit PID a pak podle něj úlohu najít. V tomto případě je však lepší uložit přímý ukazatel na struct pid, protože globální PID budou používány pouze v jaderném logu.
..._vnr()
Tito pomocníci pracují s "virtuálními" PID, tj. s ID, jak jej vidí proces. Například task_pid_vnr(tsk) vám řekne PID úlohy, jak jej daná úloha vidí (s sys_getpid()). Nezapomeňte, že pokud pracujete v jiném jmenném prostoru, bude vám to nejspíš k ničemu. Proto se používají při práci s aktuální úlohou, protože všechny úlohy vždy vidí své virtuální PID.
..._nr_ns()
Tato volání pracují s PID, jak jsou viděna z určeného jmenného prostoru. Pokud chcete získat PID nějaké úlohy (například pro nahlášení do uživatelského prostoru a pozdější vyhledání dané úlohy), můžete zavolat task_pid_nr_ns(tsk, current->nsproxy->pid_ns), což vrátí číslo, a pak úlohu najdete pomocí find_task_by_pid_ns(pid, current->nsproxy->pid_ns). Používají se v systémových voláních, když je zdrojem PID uživatelský prostor. V takovém případě může jedna úloha odkazovat na jinou v jiném jmenném prostoru.

Závěr

link

Popisované rozhraní bylo začleněno do jádra 2.6.24. Je však označeno jako "experimentální", aby jej distributoři nenasazovali, dokud se nevyřeší zbývající problémy. V období do odstranění označení "experimentální" se však v tomto API neočekávají žádné velké změny.

Související články

Jaderné noviny - 14. 11. 2007
Jaderné noviny - 7. 11. 2007
Jaderné noviny - 31. 10. 2007
Jaderné noviny - 24. 10. 2007

Odkazy a zdroje

Kernel coverage at LWN.net: November 20, 2007

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

Jaderné noviny – přehled za březen 2025
Jaderné noviny – přehled za únor 2025
Jaderné noviny – přehled za leden 2025
Jaderné noviny – přehled za prosinec 2024
Jaderné noviny – přehled za listopad 2024

Diskuse k tomuto článku

19.12.2007 19:11 JK
Rozbalit Rozbalit vše Re: Jaderné noviny - 20. 11. 2007
Odpovědět | Sbalit | Link | Blokovat | Admin
Můžete mi někdo vysvětlit, jaká je periodicita vydávání Jaderných novin?
19.12.2007 19:20 Ricardo | skóre: 27 | blog: Ricardo | Horní Suchá
Rozbalit Rozbalit vše Re: Jaderné noviny - 20. 11. 2007
náhodná ? :-D
My mind may be raving, my words may be void, but I am not afraid of being moderated below threshold!
19.12.2007 19:29 Vít Ožana | skóre: 10
Rozbalit Rozbalit vše Re: Jaderné noviny - 20. 11. 2007
náhodný občasník :)
21.12.2007 13:27 Safety Match
Rozbalit Rozbalit vše Re: Jaderné noviny - 20. 11. 2007
Ako Duke Nukem Forever - when it's done :-)

Autorovi vdaka prijemne prezitie vianocnych sviatkov.
21.12.2007 16:16 Robert Krátký | skóre: 94 | blog: Robertův bloček
Rozbalit Rozbalit vše Re: Jaderné noviny - 20. 11. 2007
Mělo by to být jednou týdně, ale dostal jsem se do skluzu, proto přes Vánoce doháním...

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