přejít na obsah přejít na navigaci

Linux E X P R E S, Vývoj jádra X. – objekty jádra

Vývoj jádra X. – objekty jádra

Minule jsme poznávali objektový model jádra. Pro svou potřebu máme v jádře k dispozici různé hotové objekty, které využijeme pro uložení a export informací o našem modulu. Podíváme se také na to, jak informovat uživatelské programy o událostech v ovladači.


Jak na to

Již v minulém dílu jsem připomenul důležitost „potomků“ struktury kobject. Je totiž dobré, máme-li data uspořádána systematicky, podle předem daných pravidel. Tím spíš, že se exportují pro programy – ty tak mohou snadno pracovat s rozličnými moduly, aniž by o nich něco speciálního věděly.

Protože se – jak vyplývá z vlastností jazyka C – potomci netvoří objektovým děděním, nýbrž kompozicí, nemáme samozřejmě k dispozici ani žádné prostředky, jak z instance předka získat potomka, tedy tu strukturu, v níž je obsažena obecnější struktura. Není však potřeba vymýšlet žádné složitosti, vývojáři už totiž vytvořili makro container_of. Minule jsme například pracovali se strukturou kset, která obsahuje též kobject. Máme-li tedy ukazatel na kobject a víme, že ho obaluje struktura kset, získáme k ní přístup takto:

struct kset* ks = container_of(ptr, struct kset, kobj);

Prvním parametrem makra je ukazatel na kobject, druhým datový typ obalující struktury a posledním pak název položky ve vnější struktuře. Toto makro je dobré si zarýt do paměti, protože se používá velice často, a to nejen pro objekty jádra. Lze ho použít vždy, když se potřebujeme dostat od vnitřní struktury ke vnější. Mimochodem, například funkce to_kset(), kterou lze též použít pro zjištění kset z kobject, toto makro používá rovněž a má navíc jen test nenulovosti ukazatele. Podobných funkcí je v jádru spousta.

Názvy některých funkcí ve strukturách se postupem času měnily. Proto pozor, kód určený pro starší jádra (např. 2.6.9) nepůjde s novými jádry zkompilovat. Týká se to hlavně příkladů v knihách a webových tutoriálech. Raději si vše předem zkontrolujte a případně opravte.

Ještě jednu důležitou věc bych rád zmínil. Často se v souvislosti s objekty jádra vyskytují nějaké „hledací“ funkce (takové, které vracejí ukazatel na objekt). Pozor na to, že pokud funkce vrátí nenulový ukazatel, inkrementuje také počitadlo referencí na daný objekt. To je samozřejmě v pořádku, jen se na to nesmí zapomenout.

Různé druhy objektů

Nyní už se můžeme pustit přímo na jednotlivé druhy objektů. Pro nedostatek místa budu muset být hodně stručný, proto odkazuji na literaturu a na hlavičkové soubory jádra, kde najdete mnohem více informací.

Sběrnice (bus)

Každé zařízení je připojeno na sběrnici – buď na nějakou konkrétní (PCI, IDE, SCSI apod.) nebo na společnou virtuální (platform). Jedna sběrnice může být připojena na jinou, např. IDE nebo USB na PCI. Sběrnice má svou strukturu bus_type, obsahující kromě názvu také subsystém (viz minulý díl), seznam zařízení a seznam ovladačů (viz dále). Struktura obsahuje také ukazatele na celou řadu funkcí pro různé operace – funkce se volají zejména při přidání nebo odebrání zařízení nebo ovladače, při uspávání a probouzení apod. Lze si to představit třeba tak, že když se něco připojí do zásuvky USB, sběrnice (pomocí svého ovladače, který komunikuje s HW řadičem USB) to zjistí, a začne se hledat způsobilý ovladač připojeného zařízení (voláním funkce match). Podobně odebrání zařízení způsobí zavolání funkce remove, a tak dále. V těchto případech se volá též uevent pro oznámení do uživatelského prostoru, ale o tom až později.

Běžný vývojář obvykle nemá potřebu vytvářet nové sběrnice, proto tyto věci vynecháme a raději se podíváme na to, co se děje v sysfs. V adresáři bus najdeme adresáře jednotlivých sběrnic. V každém jsou podadresáře devices a drivers – první obsahuje zařízení (symbolické odkazy na adresáře ve stromě pod adresářem devices), druhý pak ovladače. Například pro sběrnici IDE (adresář ide) tam jako zařízení najdeme jednotlivé fyzické disky, jako ovladače pak např. ide-disk a ide-cdrom. Seznamy samozřejmě odpovídají tomu, co obsahuje struktura bus_type.

Ovladač (device driver)

Jedná se o infrastrukturu schopnou obsluhovat nějaký druh zařízení, např. výše zmíněné disky. Pro každý druh je v systému samozřejmě jen jediný ovladač. Máme strukturu device_driver, která obsahuje vše, co s ovladačem souvisí. Kromě jiného odkaz na sběrnici, seznam zařízení a také podobnou sadu funkcí, jako má sběrnice. Je to logické, ovladač musí být informován o tom, že bylo zařízení odebráno, uspáno apod.

Na rozdíl od sběrnice zde již programátora modulu zajímá, jak vytvořit nový ovladač a co s ním provést. Základem je samozřejmě existence zmíněné struktury. Ta se pak zaregistruje pomocí driver_register() (k odregistraci je driver_unregister()). K vyhledání ovladače podle názvu lze použít driver_find() – hledá na určené sběrnici. Ovladač může mít různé atributy, pro práci s nimi platí to, co bylo řečeno minule.

Zařízení (device)

Toto jsou již data spjatá přímo s fyzickým (případně virtuálním) zařízením. Struktura device obsahuje velké množství informací, ze kterých většinou využijeme pouze část, zbytek jen v určitých případech (data pro DMA, odkaz na firmware apod.) nebo pouze nepřímo.

Data pro zařízení se vytvářejí v okamžiku, kdy je dané zařízení v systému zjištěno – tedy buď při startu jádra, nebo po připojení/zapnutí zařízení. Vytvořená a zaregistrovaná struktura se pak použije při hledání ovladače (viz výše – match). Může ji obecně vytvořit buď přímo příslušný modul nebo ovladač sběrnice, případně i nějaký další modul. Právě mechanismus dynamické vazby na ovladač zařízení umožňuje, aby to takto fungovalo.

Zařízení se registruje/odregistrovává funkcí device_register(), resp. device_unregister(). Jsou k dispozici i nízkoúrovňovější funkce (device_initialize() atd.), podobně jako pro kobject. Pro přiřazování zařízení k ovladači lze použít dvě funkce – driver_attach() (přiřadí k danému ovladači všechna odpovídající zařízení) a device_attach() (pro dané zařízení najde ovladač a přiřadí ho). Opačný efekt mají funkce device_release_driver() a driver_detach().

Třída (class)

Protože zařízení mohou být řazena do různých tříd (např. síťová, zvuková, Bluetooth), existuje v jádře podpora i pro tyto třídy. V sysfs je najdeme v adresáři class (v každém adresáři třídy jsou jednotlivá zařízení). Bloková zařízení jsou ovšem umístěna v adresáři block. Často třídu vytváří subsystém (pro obvyklé druhy) a vývojář modulu se o to tedy nestará.

Datovou strukturou pro třídu je class. Lze používat i jednodušší class_simple, nicméně ta je již zavržena a nedoporučuje se její použití v nových modulech. Struktura má podobné funkce, jaké jsme potkali u většiny předchozích objektů (registrace apod.), ovšem jednodušší je jiný způsob práce. Máme totiž funkci class_create(), která dynamicky vytvoří požadovanou třídu a zařídí vše potřebné. Pro zrušení pak použijeme class_destroy().

Zařízení třídy (class device)

Také zařízení třídy (viz výše) má svoji datovou strukturu. Jmenuje se, jak jinak, class_device. Tato struktura je nesmírně důležitá – má totiž atribut devt, který se exportuje jako soubor dev, z něhož lze přečíst major a minor číslo. Právě odsud si může udev nebo jiný program tato čísla zjistit a vytvořit pro zařízení správný soubor (typicky pod /dev). Kromě toho lze samozřejmě používat i jiné atributy podle potřeby.

Pro zařízení třídy platí totéž, co pro třídu samotnou (a zde je smysl ještě výraznější) – je lepší je vytvářet a rušit dynamicky, pomocí class_device_create() a class_device_destroy(). Při vytváření je jedním z parametrů též ukazatel na strukturu fyzického zařízení (struct device), ke kterému toto zařízení třídy přísluší.

Stejně jako u tříd, ani zde není často vůbec potřeba zabývat se vytvářením zařízení třídy. Píšu o tom ovšem proto, že když už vytváříme vlastní třídu (pro nějaká speciální zařízení), budeme vytvářet i instance zařízení.

Celé je to ještě o něco složitější (máme např. ještě rozhraní tříd a další věci), ale na to tu už bohužel není místo. Musíme si totiž povědět něco o přenosu událostí do uživatelského prostoru.

User-space events

Když někde ve starším jaderném kódu narazíte na termín hotplug, bude se jednat o totéž. Při operacích souvisejících se zařízeními a ovladači často potřebujeme dát uživatelským programům najevo, že se něco stalo. Například můžeme potřebovat vytvořit soubor zařízení, načíst firmware, vytvořit přípojný bod pro filesystém, indikovat zařízení uživateli apod. Původní název měl naznačovat, že bylo něco připojeno/odpojeno „za horka“ (na USB apod.). Protože škála využití je mnohem širší, používá se nyní nový, vhodnější název. Podstata ovšem zůstala stejná – spustí se nějaký program a předají se mu odpovídající parametry.

Jedná se o poměrně složitý aparát, proto se podíváme jen na základy. Události může generovat celá řada objektů. Základní mechanismus je umístěn ve struktuře kset a je možné ho různě upravovat. Jsou tam tři ukazatele na funkce – filter (filtrace událostí podle objektů), name (získání názvu) a uevent (samotné vytvoření události).

Událost se generuje funkcí kobject_uevent(). O naprostou většinu událostí se není třeba vůbec starat. Generují se automaticky, například při registraci třídy, zařízení třídy a dalších objektů. Na uživatelské straně událost vyústí ve spuštění programu (výchozí je /sbin/hotplug, což může být jak klasický program, tak i skript) a/nebo poslání zprávy přes netlink (podle konfigurace). Mnohem efektivnější je samozřejmě použití netlinku (neznamená nákladné spouštění procesu), je proto preferováno všude tam, kde to jde.

Netlink je moderní metoda univerzální obousměrné komunikace mezi jádrem a uživatelskými programy. Vyznačuje se snadnou použitelností a velkou robustností. Z uživatelské strany se používá standardní socketové rozhraní (stejné jako pro síťovou komunikaci), na straně jádra je k dispozici sada funkcí. Netlink podporuje jak unicast, tak i multicast a broadcast. Často se používá pro složitější síťovací funkce (řízení firewallu, filtrace paketů, logování apod.), ale jeho použitelnost je mnohem širší.

Do uživatelského prostoru putují různé informace. Vždy je to 64bitové sekvenční číslo události, její název (např. „add“, „remove“, „change“), cesta k zařízení a název subsystému. Na základě těchto parametrů lze snadno zjistit, co a kde se stalo a zařídit se podle toho. Když třeba přidáme nové zařízení třídy, pošle se událost „add“ s příslušnou cestou, kde si pak reagující program (např. udev) kupříkladu zjistí čísla zařízení a vytvoří podle nich příslušný soubor. Jak jednoduché, elegantní a flexibilní...

Ač velice nerad, zatím jsem vás musel ošidit o praktické ukázky. Vysvětlované věci ale jsou tak provázané, že bych to rád ukázal pohromadě. Příští díl bude tedy prakticky celý věnován tomu, jak toto vypadá ve skutečném modulu. Teoretická část bude velice krátká a bude se zabývat načítáním firmwaru – to je totiž věc, bez které se u mnoha zařízení neobejdeme.

Nahoru

Odkazy

Přidat téma diskuse

Nejsou podporovány žádné značky, komentáře jsou jen čistě textové. Více o diskuzích a pravidlech najdete v nápovědě.
Diskuzi můžete sledovat pomocí RSS kanálu rss



 
 

Top články z OpenOffice.cz

Lukáš Jelínek

Lukáš Jelínek

Dlouholetý člen autorského týmu LinuxEXPRESu a OpenOffice.cz. Vystudoval FEL ČVUT v oboru Výpočetní technika. Žije v Kutné Hoře, podniká v oblasti IT a zároveň pracuje v týmu projektu Turris. Ve volném čase rád fotografuje, natáčí a stříhá video, občas se věnuje powerkitingu a na prahu čtyřicítky začal hrát tenis.


  • Distribuce: Debian, Kubuntu, Linux Mint
  • Grafické prostředí: KDE

| proč linux | blog