Proč objekty?
Většina programovacích jazyků obsahuje ve větší či menší míře podporu objektového programování. Je to logické, tento přístup přináší pro vývojáře nemalé výhody. Ze stejného důvodu si objekty našly cestu i do jádra operačního systému - a to přesto, že se zde programuje v jazyce C, který objektově orientovaný není.
Prakticky každá součást jádra, každý subsystém, každý ovladač zařízení pracuje s daty, která by měla být nějak logicky organizována a která se nějak poskytují navenek nebo se naopak zvenku přijímají. K tomu se právě hodí objekty - můžeme pracovat s třídami objektů, využívat dědičnost a zapouzdření atd.
Velkou výhodou objektů v Linuxu je jejich těsná vazba na sysfs. Často řešeným problémem je totiž zjišťování informací o ovladači zařízení (stav, statistické informace apod.) a jeho konfigurace. Původně se tyto činnosti realizovaly pomocí systémového volání ioctl(), což je poměrně složité a nepříliš bezpečné, a navíc to vyžaduje otevřený deskriptor zařízení. Proto se (po různých pokusech, jak to řešit) později vyvinul (na systémech BSD) mechanismus sysctl(), pracující s objekty MIB, který na Linuxu využíval data exportovaná přes procfs a byl už určitým zárodkem dnešního objektového modelu. S jádry řady 2.6 konečně přišel do Linuxu skutečný model v dnešní podobě, exportující data přes sysfs.
Hlavním "uživatelem" sysfs je program udev. Odtud čerpá informace o tom, jaké soubory zařízení (pod /dev) má vytvářet a jaké další činnosti případně dělat. Informace ze sysfs používá též démon HAL, který pak podle nich (a samozřejmě podle nastavených pravidel) upravuje soubor fstab, vytváří přípojné body a provádí různé další operace. Těmto dvěma programům můžeme připravit pravidla, podle kterých se bude měnit chování systému v závislosti na datech exportovaných z našeho modulu.
Starší metody se neruší, jen se více zaměřují na své původní určení. Proto ioctl() má smysl používat především tam, kde se jedná o věci související s konkrétním otevřeným deskriptorem. sysctl() se postupně opouští, zatím v něm zůstávají nastavení obecné povahy. Pro data vázaná na ovladač jako celek nebo na instance zařízení je nejvhodnější používat současný objektový model a sysfs.
Architektura objektového modelu
Kdo zná aspoň základy objektově orientovaného programování (OOP), velmi rychle pozná podobnost jednotlivých součástí objektového modelu jádra s tím, co se používá při objektovém programování. Než se k tomu ale dostaneme, nejprve bych uvedl další věci, které jsou součástí implementace jaderného objektového modelu a které jsou rovněž důvodem, proč tento model používat:
- počítání referencí na objekty;
- správa seznamů objektů;
- zamykací mechanismy;
- signalizace událostí do uživatelského prostoru.
Přítomnost těchto implementací umožňuje ušetřit si práci a zaměřit se jen na to, co je pro daný modul specifické. Podívejme se nyní na to, jak je objektový model navržen.
Objekt - kobject
Tato struktura je základem objektového modelu a obsahuje vše potřebné pro práci s objektem. Instance struktury má svůj název, počitadlo referencí, odkaz na rodiče a na třídu (viz dále) a infrastrukturu pro vložení do seznamu. V sysfs je objekt reprezentován jako adresář. Přímo v této podobě se s ním většinou přímo nepracuje, bývá obvykle součástí větších datových struktur (kompozice použitá místo dědění).
Metody objektu (jak je známe z OOP) jsou zde nahrazeny funkcemi, které mají jako parametr ukazatel na objekt. Je to ovšem trochu složitější. Pro "vytvoření" objektu jsou tu funkce kobject_init(), kobject_add() a kobject_register(). Třetí z nich je v podstatě postupným zavoláním těch prvních dvou. kobject_init() pouze inicializuje objekt (pozor - paměť by měla být vždy nejprve vynulována!), kobject_add() ho pak přidá do hierarchie a zajistí jeho export do sysfs. Název objektu se nastavuje funkcí kobject_set_name(), musí se to ovšem udělat ještě před zavoláním kobject_add() nebo kobject_register().
Opačný proces, tedy odstranění, provedou funkce kobject_del() (pouze odebere z hierarchie a ze sysfs) a kobject_unregister() (navíc i dekrementuje čítač referencí). Přímo s referencemi se pracuje pomocí funkcí kobject_get() a kobject_put(). První počitadlo o jedničku zvýší, druhá sníží - a pokud počet referencí klesne na nulu, zavolá úklidovou funkci (ještě o tom bude řeč). Tyto funkce je nezbytně nutné volat pro každou manipulaci s objektem, aby nám pod rukama nečekaně nezmizel.
Zmíněné funkce se většinou nevolají přímo. Volají je podobné funkce vztažené ke struktuře, jíž je kobject součástí. Brzy se k tomu dostaneme. I když to dosavadní popis neříká (ani to není úplně nutné), předpokládá se, že objekty budou alokovány dynamicky. Je to z toho důvodu, že většinou nelze předem určit, jak a kdy se přesně budou objekty vytvářet a rušit. Právě proto se také používá počitadlo referencí - objekt je zrušen (i včetně uvolnění paměti) v okamžiku, kdy počet referencí klesne na nulu.
Třída - kobj_type
Zatímco předchozí datová struktura se vztahuje k instancím objektů, tato představuje objektovou třídu. Obsahuje ukazatele na úklidovou funkci, na strukturu sysfs operací (vstup/výstup dat) a na pole výchozích atributů (datových položek) objektu.
Atributy objektů mohou být textové nebo binární. Výrazně jednodušší je práce s textovými atributy, proto jim dáváme přednost. Pro přenos většího množství dat (pokud ho chceme provádět touto cestou) se ale hodí binární atributy - už proto, že délka dat je u textového atributu omezena na 1 stránku paměti (a podle doporučení by měl textový atribut obsahovat jedinou hodnotu). Binární atributy se musí vytvářet a rušit explicitně (nemohou být výchozí).
Atribut se v sysfs tváří jako soubor, kterému můžeme nastavit přístupová práva. sysfs operace slouží ke komunikaci s uživatelskými programy (ty pracují s atributem jako s normálním souborem - čtení a zápis se transformuje na zavolání právě těchto funkcí). Úklidová funkce již byla zmíněna - kromě jiného je její obvyklou náplní uvolnění paměti alokované objektem.
Potřebujeme-li zjistit, do jaké třídy patří nějaký objekt, máme k tomu funkci get_ktype(). Proč se má používat tato funkce místo přímého sahání na data, o tom bude řeč za chvíli. Nastavování třídy objektu obvykle vůbec řešit nemusíme, protože se o to postarají "potomci" kobject.
Seznam - kset
Jedná se o seznam objektů stejné třídy. Implementováno je to jako standardní zřetězený seznam objektů, z nichž každý odkazuje navíc na strukturu kset. kset je rozšířením struktury kobject - obsahuje (kromě seznamu) také odkaz na subsystém a na strukturu pro notifikace do uživatelského prostoru (o obojím bude řeč později). Dříve se pro tyto notifikace používal termín hotplug (odvozené od toho, že se generovaly často při připojení/odpojení zařízení "za horka", např. na USB), u novějších jader se však pracuje s výstižnějším termínem user-space events (události oznamované do uživatelského prostoru). Říkám to proto, že se změnilo i označení symbolů a starší jádra obsahují ty původní.
Pro práci s kset se používají funkce s podobným pojmenování i účelem jako pro kobject. Zajímavou a důležitou funkcí pak je kset_find_obj(), která v seznamu hledá objekt podle názvu.
Objekty vložené do seznamu, kromě zmíněného odkazu, na kset, obsahují většinou rovněž odkaz na obsažený kobject jakožto na svého rodiče (není to nutné, ale má to logiku).
Subsystém - subsystem
Není to v podstatě nic jiného než kset se semaforem pro serializaci přístupu k seznamu. Subsystémů nebývá v jádře mnoho, protože se obvykle používají jen ty stávající (např. block nebo devices) a žádné nové se nevytvářejí.
I pro subsystém se používají funkce podobné těm od struktury kobject, ale běžný vývojář modulu ani nemá potřebu s nimi přijít do styku.
Symbolické odkazy
Nejde o žádné objekty ani nic podobného. Mnohdy je z praktických důvodů (použití v uživatelských programech) výhodné, aby byl v objektu (= adresáři) k dispozici symbolický odkaz někam jinam. Právě k tomu slouží symbolické odkazy, které se i v sysfs chovají stejně jako v jiných souborových systémech.
Máme funkce sysfs_create_link() a sysfs_remove_link(), jejich význam je zcela zřejmý. Symbolický odkaz se vytváří vždy na nějaký objekt, odkaz na atribut (soubor) nebo dokonce do jiného souborového systému není možno vytvořit.
Atributy
Jak jsem již řekl dříve, atributy jsou vlastně soubory v sysfs, přes které uživatelské aplikace komunikují s modulem. Existují dva druhy atributů - výchozí (viz kobj_type) a ostatní. Výchozí atributy patří dané struktuře kobj_type a jsou tedy společné všem objektům, které jsou "instancemi" této třídy. O jejich přípravu se stará inicializátor třídy.
Můžeme ale vytvářet (klidně i dynamicky) a používat i další atributy. Do struktury kobject je přidáme pomocí sysfs_create_file(), odstraníme pak funkcí sysfs_remove_file(). Pozor ale, že s nimi musí počítat i funkce sysfs operací použité v kobj_type!
V tuto chvíli se určitě někdo ptá, kde jsou uložena ta data, která přes sysfs exportujeme. Tuto otázku objektový model jako takový neřeší. Je na vývojáři, jak se s tím vypořádá. Data mohou být např. v položkách struktury rozšiřující kobject, mohou být vytvářena "on-demand" (tj. v okamžiku zavolání čtecí funkce), záleží na tom, co je cílem. Každého jistě napadne, jak to vyřešit třeba při počítání přenesených paketů, počtu měření, zjišťování stavu zařízení, nebo naopak při nastavování nějaké konfigurační hodnoty.
K čemu je to dobré? Tohle byla zatím jen nudná teorie - byla však nutná k pochopení mnohem zajímavějších věcí, ke kterým se dostaneme příště. Skutečná síla se totiž skrývá teprve v "potomcích" uvedených struktur. Příští díl seriálu bude věnován využití objektového modelu pro uložení dat a pro komunikaci s programy. Také se krátce podíváme i na přenos událostí do uživatelského prostoru.