
Jak to chodí v jádře aneb napište si vlastní ovladač (1)
Chcete se blíže ponořit do tajemství Linuxového jádra? Chcete blíže poznat jak
celé jádro pracuje? Chcete si sami napsat svůj vlastní ovladač? Odpovíte-li
alespoň jednou ano mám pro Vás novou sérii článku zabývající se právě výše
uvedeným tématem - Linuxovým jádrem.
Linux
Čas plyne a popularita systému Linux stále roste a s ní samozřejmě roste
zájem dodávat své produkty s ovladači pro tento systém. Linux zahrnuje široké
portfolio podporovaných platforem včetně HW, který se na těchto platformách
běžně používá. Příkladem je klasické PC a síťová karta. Chceme-li aby náš
Linux uměl využívat naši síťovou kartu, je nutné aby někdo napsal ovladač,
který pomocí speciálního API nabídne funkce této karty jádru. Ovladač je svým
způsobem černá skříňka, která zaštiťuje funkce daného HW na té nejnižší úrovni
a pomocí známého API nabízí tyto funkce jádru. My se pokusíme tuto černou
skříňku alespoň částečně otevřít ...
Rozhodl jsem se tuto problematiku přiblížit čtenářům a blíže se zaměřit na
problematičtější části. Jako vodítko si tato série vezme knihu Linux Device
Drivers od Alessandra Rubiniho a Jonathana Corbeta. Je to již druhé uspěšné
vydání, které pokrývá stabilní jádra řad 2.[0,2,4].x. Knihu vydalo
nakladatelství O'Reilly a to pod licencí GNU Free Documentation License verze 1.1. Kniha samotná je
taktéž dostupná v elektronické podobě na této adrese a to
včetně PDF a XML verze. Tuto knihu vřele doporučuji všem zájemcům
o problematiku.
V rámci této série narazíme na několik pojmů, které se obtížně překládají
do českého jazyka beze změny významu. Je jich malé procento a proto jsem se
z důvodu správného významu rozhodl ponechat je v angličtine.
Internetové zdroje
Za zmínku stojí základní internetové adresy zabývající se touto
problematikou a jejich krátký seznam nabízím zde:
- http://www.kernelnewbies.org
Tento web je orientován na začátečníky a najdete zde odkaz na IRC kanál
kde lze nalézt pomoc zkušenějších při řešení různých problémů.
- http://www.kernel.org
Toto je hlavní stránka vývojářů kernelu a dostanete zde také seznam
mirrorů kde všude lze najít nejnovější verze Linuxového jádra.
- http://www.linux.it/kerneldocs
Na této stránce je publikováno několik článků Alessandra týkajících se
jádra.
- http://www.linux-mm.org
Jak již název napovídá, web se orientuje na tzv. "memory management"
a najdete zde spousty odkazů týkajících se této problematiky.
- ... a spoustu dalších, které jsou k nalezení v online verzi knihy či
přímo na internetu.
Ovladač
Jako programátor si budete muset vybrat čas který budete chtít samotnému
programování zasvětit a tím, co bude výsledný ovladač umět. Alessandro nazval
správný ovladač "flexibilním" a to z toho důvodu, že toto slovo svým způsobem
zdůrazňuje jednu z hlavních myšlenek UNIXového designu a to, že ovladač by měl
nabízet mechanismus jak dané zařízení využít a to bez jakékoli přístupové
politiky, která se řeší na vyšší úrovni (neplatí vždy, ale ve většině
případů).
Jako jeden z mnoha příkladů můžeme uvést způsob práce grafického rozhraní
Linuxu. Za ovladač zde můžeme označit X server, který se stará o to, aby přes
předem jasně definované API nabídl funkce grafických karet (nabízí mechanismus
pro jejich ovládání bez definování přístupu). Druhý z dvojice je okenní
manažer, který zajišťuje výše zmíněnou přístupovou politiku bez toho aniž by
něco věděl o tom jak právě využívaný HW pracuje (pro jeho ovládání používá
výše zmíněné již předem definované API X serveru). Co nám to přináší? Lidé
takto mohou používat různý HW a přitom jim běží stále stejný okenní
manažer. Obráceně mohou na stejném HW provozovat několik různých okenní
manažerů či celých desktopových systémů jakými jsou KDE či GNOME. Těchto
příkladů je spousty a zájemce odkazuji na stranu 2 LDD (takto budu dále
označovat knihu Linux Device Drivers, druhé vydání).
Většina Vámi napsaných ovladačů by měla nabízet funkce daného zařízení bez
přístupové politiky, měla by podporovat synchronní a asynchronní operace,
schopnost několikanásobného využití ovladače, schopnost prozkoumat dostupné
možnosti zařízení a měla by obsahovat pouze nezbytné množství vrstev pro
zjednodušené ovládání. Budete-li se držet těchto zásad Váš ovladač se bude
nejen jednodušeji používat, ale určitě se bude i jednodušeji udržovat co se
programátorské stránky týče.
Ještě se krátce zmíním o tom, že pokud je daný HW podporován na více
platformách, měl by být ovladač psán tak, že poběží bez problémů na
32-bitových, 64-bitových systémech a na různých platformách, které se liší
v ukládání čísel v paměti, tj. little a big endian. Ovladač by také měl být
napsán tak, že poběží na SMP (více procesorů) systémech. Tyto informace uvádím
pouze v krátkosti neb se tímto budeme zabývat v některém z dalších článků této
série.
Rozdělení ovladačů
Ovladače jako takové většinou rozdělujeme do tří tříd (podle druhu zařízení, které obsluhují):
- Znakové (character devices)
Přístup k ním je podobný přístupu ke
klasickým souborům, tj. je proudově orientovaný. Co to znamená? K těmto
zařízením přistupujeme jako k proudu znaků a ovladač ve většině případů
implementuje funkce open, read, write a close. Rozdíl mezi klasickým
souborem a tímto zařízením přece jenom existuje - v klasickém souboru
můžete měnit aktuální pozici směrem dozadu, tj. číst data zpětně,
atd. kdežto u znakových zařízení přistupujete k datům pouze
sekvenčně. Existují zde vyjímky, například frame grabber, kde je
aplikaci umožněn přístup k celému sejmutému obrázku pomocí mmap či
lseek. Ještě jsem opomenul připomenout, že k znakovému zařízení se
přistupuje pomocí souboru (jak je v UNIXu zvykem - vše je soubor)
a seznam těchto zařízení můžete získat pomocí příkazu ls -l /dev | grep
^c
- Blokové (block devices)
Blokové zařízení se liší od znakového
"pouze" tím, že čtení či zápis dat je prováděn pouze v blocích, které
mají ve většině případů velikost 1kB (mohou mít ale libovolnou velikost
představující jakoukoli mocninu čísla 2). Tento blokově orientovaný
přístup je uživateli neviditelný, protože je implementován uvnitř jádra
a uživatel má možnost přečíst/zapsat libovolné množství znaků z/na
zařízení. Mezi tyto zařízení patří například pevné disky
a diskety. Z toho také plyne, že dané zařízení umožňuje hostit souborový
systém, který může být připojen (mount) a využíván systémem. Přístup
k těmto zařízením, je stejně jako u znakových, pomocí souborů a jejich
seznam je možné získat analogicky pomocí příkazu ls -l /dev | grep
^b
- Síťový interface (network interface)
Pro síťové transakce je
používáno toto zařízení, které je schopno výměny dat mezi dvěma a více
počítači. Většinou se jedná o různé síťové karty, ale není tomu tak
vždy, může se jednat i o čistě softwarové zařízení kterým je kupříkladu
tzv. loopback. Toto zařízení má na starosti odesílání/přijímání dat a je
řízeno subsystémem jádra bez znalosti toho, co to je za data. Za příklad
si můžeme vzít jakékoli dvě a více síťových služeb, například FTP a SSH,
které obsluhuje stejné zařízení (posílá a příjímá data) bez toho, aniž
by věděl, že tyto data jsou FTP přenosu a tyto pro SSH seanci. Vyšší
vrstva tyto datové proudy přetransformuje na jednoduché datové pakety
o které se již postará síťové zařízení. Jelikož síťový interface není
proudově orientovaný, nelze toto zařízení jednoduše mapovat jako tomu
bylo u předchozích dvou. K tomu účelu slouží přiřazené unikátní jméno
(jako eth0, ...), které již ale nemá korespondující záznam na
disku. Komunikace jádra a těchto interfaců je odlišná od znakového (či
blokově) orientovaného zařízení a shoduje se pouze ve dvou funkcích read
a write, které se týkají přenosu paketů. Seznam síťových interfaců lze
získat napříkladem pomocí příkazu ifconfig
- Další zařízení
V Linuxu existují samozřejmě další třídy
ovladačů kterými jsou například USB, FireWire, SCSI či I2O. Jak se
k těmto zařízením přistupuje? Stejně jako ke znakovým (či blokovým)
s tím, že interní uspořádání ovladače je rozdílné od klasických
znakových (či blokových).
Bezpečnost
Bezpečnost je jednou z nejdůležitějších věcí moderní doby. Většinu
ožehavých témat prodiskutujeme až na ně narazíme a nyní se seznámíme pouze
s hlavními koncepty.
Jeden pohled je, že systém se bude snažit někdo záměrně zneužít. Například
programátor, který má vždy mnohonásobně větší sílu než běžný uživatel, může
nezkušenému uživateli podstrčit "správně" napsaný modul, který tento člověk
důvěřive zavede do systému. Pokud daný uživatel nemá oprávnění administrátora
(roota), nemůže se nic stát, protože volání create_module kontroluje zda je
uživatel oprávněn modul zavést či nikoli. Problém ale nastává v případě, že
nezkušený uživatel pracuje pod rootem (jako že jich zde je neuvěřitelně mnoho)
a tento modul může bez problémů zavést. Pokud Vám někdo pošle modul, který je
pouze v binární podobě a tedy nemáte možnost, zkontrolovat jeho chování ve
zdrojovém souboru, nikdy ho do jádra nenahrávejte, protože tím může útočník
získat neomezenou moc nad vaším systémem.
Druhý pohled je ten, že do Vašeho systému se někdo může nabourat
náhodou. Při vhodné konstelaci hvězd se tak za starých časů toto stávalo
docela často. Programátor by si měl dát pozor na věci, které jsou (při
volnosti jakou nabízí jazyk C) docela běžné. Například zápis do bufferů bez
kontroly jejich velikosti, resp. velikosti dat, které zapsal. Je zde totiž
velmi pravděpodobné, že bude zapsáno více dat než je velikost bufferu
a přepíše se kód, který je uložen v paměti za bufferem a útočník může tyto
chyby zneužít. Tato chyba se označuje jako buffer overrun. Další věc je
důsledná kontrola jakéhokoli vstupu od uživatele a další prací s ním. Nikdy
nedůvěřujte datům, které Vám "podstrčí" uživatel. Jedna z posledních věcí je
inicializace alokované paměti - měla by být prováděna vždy po alokaci, protože
nikdy nemůžete vědět co na daném místě v paměti bylo uloženo.
Vyjímkou v zavedení přístupové politiky do ovladače mohou být části, které
obstarávají nahrání nového firmware na zařízení, atp. Tato činnost může
ovlivnit chod celého systému a měla by být omezena pouze pro privilegované
uživatele.
Verze
V rychlosti se zmínímo o verzích Linuxového jádra. Každá stabilní řada má
sudé číslo 2.0.x, 2.2.x, 2.4.x a každá vývojová řada má lichá čísla 2.1.x,
2.3.x, 2.5.x. My se budeme zabývat pouze jádry stabilní řady 2.4.x a občas se
podíváme na stabilní jádra starších řad. Vy máte možnost ve svém ovladači
kontrolovat verzi jádra do kterého se nahrává a tím tak zabezpečit dobrou
funkčnost v jádrech starších či novějších řad.
Rozdělení jádra
Než se pustíme dále podíváme se, jakým způsobem nám funce jádra rozdělil
Alessandro. K tomuto účelu použijeme jeho schéma 1-1 (strana 5 LDD) z jeho
knihy.
- Management procesů
Ve stručnosti se tato část jádra zabývá tím,
že řídí procesy využívající jeden či více procesorů daného
počítače. Vytváří, ruší procesy a obstarává jejich spojení s okolním
světem (signály, roury, IPC). Řízení procesů samotných obstarává
scheduler.
- Management paměti
Paměť je jedním z nejdůležitějších zdrojů
systému a správný přístup k ní kriticky ovlivňuje výkon. Jádro samo
o sobě vytváří virtuální adresní prostor, který je využívám všemi
aplikacemi a jádro potom samo rozhoduje kde bude která část virtuální
prostoru uložena (paměť, swap, ...). K tomuto virtuálními prostoru se
přistupuje pomocí sady klasických funkcí malloc/free a dalších.
- Souborové systémy
UNIXový systém je založen na systému
souborovém. V drtivé většině případů je vše chápáno jako
soubor. Z tohoto důvodu jádro vytváří abstraktní strukturovaný souborový
systém nad dostupným hardwarem, který je všemožně využíván. Linux
podporuje většinu známých souborových systémů počínaje ext2 konče FAT či
NTFS.
- Ovladače zařízení
Většinu systémových operací je nutno mapovat
na fyzické zařízení. Jsou zde vyjímky jako procesor, paměť a několik
málo dalších věcí. Jádro nabízí své vlastní API pro systémové operace,
které musí implementovat kód nabízející služby zařízení systému a tím je
ovladač jádra. Chce-li uživatel využívat nějaké zařízení, musí do
jádra zavést jeho ovladač aby jádro vědělo, jak přetransformovat
systémové operace na operace, kterým dané zařízení rozumí a je schopno
je vykonat. Tato problematika je hlavním tématem této série.
- Síť
Síťová vrstva musí být kontrolována samotným jádrem,
protože síťové operace nejsou spojovány s běžícími procesy. Proč? Jak
jsem se výše zmiňoval, veškerá data co chceme poslat po síti jsou
přetransformována vyšší vrstvou na data, kterým rozumí síťová vrstva
a ta je rozesílá okolním počítačům. Odesílání a hlavně přijímání paketů
patří mezi asynchronní operace a než jsou přijmuté údaje postoupeny vyšší
vrstvě (aplikaci), je nutné tyto data shromážďovat, třídit,
identifikovat a poté postoupit dál. Routování paketů, které je
s touto problematikou spojeno je taktéž obstaráváno jádrem.
Nyní máme hrubou představu o tom co je ovladač, jaké by měl mít znaky
a také máme velmi hrubou představu o tom jak je celé jádro rozděleno. Pro
začátek nám tyto informace stačí a podrobněji se tím budeme zabývat jakmile na
danou problematiku narazíme.
V příštím článku se podíváme na to, jakým způsobem sestavit ovladač, zavést
ho do systému a případně ze systému odstranit.
Další části seriálu:
|