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

Linux E X P R E S, Vývoj jádra IV. - základní operace

Vývoj jádra IV. - základní operace

Moduly mohou poskytovat služby dovnitř jádra, ale velice často i navenek (uživatelským programům). K tomu je ovšem potřeba vytvořit rozhraní, přes které se k modulu bude přistupovat. Často se hovoří o registraci zařízení, byť se často nejedná o ovladač skutečného (fyzického) zařízení.


Major a minor čísla

Kdo trochu lépe zná systém Linux, určitě se setkal s tzv. major a minor čísly. Právě ta se používají jako identifikace zařízení v systému. Major číslo je jedinečné pro třídu zařízení, kdežto minor pro jeho instanci. Např. major číslo 6 znamená třídu paralelních portů, minor čísla 0, 1, 2 atd. pak označují jednotlivé porty.

Major čísla se buď volí staticky (pro standardní druhy zařízení, třeba disky, různé porty, Bluetooth atd. – bližší informace najdete v souboru devices.txt v dokumentaci jádra), nebo je jádro může přidělovat dynamicky (pro speciální zařízení a k experimentálním účelům). Pro naše zkušební účely budeme vždy používat právě dynamické přidělování, abychom vyloučili případné kolize mezi moduly.

Vytváření speciálních souborů zařízení prošlo během let velkým vývojem. Původně se používaly staticky vytvářené soubory – adresář /dev býval plný souborů, z nichž většina se nikdy nepoužila, navíc se to nehodilo pro dynamicky přidělovaná čísla. Proto vznikl nejprve souborový systém devfs, kdy se tyto soubory vytvářely přímo v jádře, a systém jako celek se připojil na /dev. Kvůli řadě negativních vlastností se ale v jádře dlouho neohřál a byl nahrazen metodami, které dynamické vytváření souborů zařízení řeší v uživatelském prostoru (mimo jádro).

Přidělování minor čísel i další operace s nimi jsou plně v režii modulu. Jádro samotné tato čísla nezajímají, stará se pouze o rezervaci jejich rozsahu. U jednoduchých modulů si lze vystačit s jediným minor číslem, není tedy potřeba se zabývat jejich obsluhou.

V jádře se používají dvě skupiny číslování – zvlášť pro znaková a bloková zařízení (znaková komunikují přes proudy bajtů, kdežto bloková pomocí bloků dat). Dvě zařízení z různých skupin mohou mít stejné major i minor číslo, přesto se jedná o různá zařízení (např. pro major 3 jsou to pseudoterminály a disky). V tomto dílu seriálu bude řeč pouze o znakových zařízeních, k těm blokovým se dostaneme později.

Registrace zařízení

První věc, kterou je nutno udělat pro zpřístupnění modulu navenek, je registrace čísel pro nové virtuální zařízení. Použijeme k tomu funkci alloc_chrdev_region(), která udělá přesně to, co potřebujeme – tedy zajistí dynamické přidělení major čísla a rezervuje úsek minor čísel.

Čísla máme přidělena, proto si necháme vytvořit potřebnou datovou strukturu pro zařízení. Zavoláme tedy funkci cdev_alloc() - ta strukturu vytvoří a vrátí ukazatel. Lze také použít staticky vytvořenou strukturu (v případech, kdy bude součástí nějaké větší struktury) a inicializovat ji, zájemci nechť nahlédnou do dokumentace.

Dalším krokem je přiřazení struktury souborových operací. K této struktuře se velice brzo dostaneme – nyní bych chtěl jen zmínit známou věc, že se zařízením se pracuje jako se souborem. Proto tedy potřebujeme souborové operace, i když nemusíme implementovat zdaleka všechny. Nyní tedy předpokládejme existenci této struktury, ukazatel na ni přiřadíme příslušné proměnné ve struktuře zařízení (viz dále).

Komunikace s modulem prostřednictvím souborových operací je jen jednou z možných. Lze komunikovat i přes speciální soubory vytvářené v souborovém systému procfs, anebo přes volání sysctl (což je z hlediska modulu prakticky totéž). V jádrech 2.6 se ale místo toho používá komunikace přes filesystém sysfs, poskytující mnohem více možností (díky těsné vazbě na objektový model jádra). Přesto ale komunikace přes soubor zařízení zůstává základní metodou.

To ale stále není všechno. Zařízení „ožije“ teprve v momentě, kdy ho přidáme do systému funkcí cdev_add(). V argumentech funkce určíme, pro jaký rozsah minor čísel se příslušná struktura použije – můžeme jich mít tedy víc, a podle minor čísla jádro odliší, které zařízení (a tedy která množina souborových operací) se použije. Tento mechanismus využijeme tehdy, implementujeme-li pro různá minor čísla odlišně se chovající virtuální zařízení. Při odregistrování zařízení se postupuje obráceně – nejprve se funkcí cdev_del() odebere zařízení (a též uvolní paměť, pokud byla struktura alokována dynamicky) a pak se zavolá unregister_chrdev_region() na odregistraci oblasti čísel zařízení.

Struktura pro souborové operace

Modul může (podle potřeby) implementovat některé souborové operace, a jiné nemusí. Prakticky vždy se musí implementovat otevření a uzavření souboru (bez toho nelze rozumně provádět další operace), velice často využijeme funkci ovládání zařízení (ioctl) a také samozřejmě čtení (read) a zápis (write).

Budeme-li vždy používat stejné operace, můžeme příslušnou strukturu vytvořit staticky. Přiřadíme ukazatele na příslušné funkce, pro neimplementované operace ponecháme položky prázdné (nulové). Vždy ale ve struktuře nastavíme vlastníka (tedy aktuální modul), aby jádro nepovolilo uvolnění modulu, který někdo používá. Oproti dřívějším řadám jader se však nemusíme starat o samotné sledování použití – jádro si to ošetřuje samo.

Podívejme se na příklad. Rozšiřuje náš jednoduchý modul z předminulého dílu seriálu do podoby, že ho lze „používat“ (v rámci jeho schopností) z uživatelských programů. V příkladu je uvedeno jen to, co je nové nebo co se liší.

#include 
#include 
#define MYMODULE_NAME "mymodule"
#define MINOR_COUNT 1
static int mymodule_open(struct inode* pinode,
struct file* pfile)
{
printk(KERN_INFO MYMODULE_NAME ": zarizeni otevreno\n");
return 0;
}
static int mymodule_release(struct inode* pinode,
struct file* pfile)
{
printk(KERN_INFO MYMODULE_NAME ": zarizeni uzavreno\n");
return 0;
}
static struct file_operations mymodule_fops = {
.owner = THIS_MODULE,
.open = mymodule_open,
.release = mymodule_release,
};
static dev_t s_dev = MKDEV(0,0);
static struct cdev* s_pdev = NULL;
static int __init mymodule_init(void)
{
int res = alloc_chrdev_region(&s_dev, MINOR(s_dev), MINOR_COUNT, MYMODULE_NAME);
if (res < 0)
goto alloc_failed;
s_pdev = cdev_alloc();
if (s_pdev == NULL) {
res = -ENOMEM;
goto cdev_failed;
}
s_pdev->ops = &mymodule_fops;
res = cdev_add(s_pdev, s_dev, MINOR_COUNT);
if (res < 0)
goto add_failed;
printk(KERN_INFO
"mymodule: modul inicializovan (major=%u)\n",
MAJOR(s_dev));
return 0;
add_failed:
cdev_del(s_pdev);
cdev_failed:
unregister_chrdev_region(MINOR(s_dev), MINOR_COUNT);
alloc_failed:
return res;
}
static void __exit mymodule_cleanup(void)
{
cdev_del(s_pdev);
unregister_chrdev_region(MINOR(s_dev), MINOR_COUNT);
printk(KERN_INFO "mymodule: modul uvolnen\n");
}

Všimněte si, že při selhání některé z funkcí se systém vrátí do původního stavu. K tomu lze s výhodou použít skoky na návěští (příkaz goto), přestože se v běžných programech jejich používání nedoporučuje. Co v příkladu (kvůli místu) chybí, jsou chybové zprávy vypisované do logu – nechť si je každý do příslušných míst doplní. Na ukázku implementace dalších souborových operací tu bohužel nezbývá místo, vrátíme se k nim později.

Vyzkoušení modulu

Nyní přichází okamžik, kdy modul vyzkoušíme z programu. K tomu je nejprve nutné vytvořit soubor zařízení. Později se dostaneme k metodám, jak tuto činnost provádět automaticky – nyní ale soubor vytvoříme ručně. Po zavedení zjistíme jeho major číslo (vypíše ho do logu, také ho lze zjistit např. ze souboru /proc/devices a dalšími cestami) a zavoláme tento příkaz (zde uvedené major číslo 253 se nahradí skutečným):

mknod mydevice c 253 0

Příkaz vytvoří v aktuálním adresáři soubor mydevice, který zpřístupní naše zařízení. Připomínám, že standardně má právo vytvářet tyto soubory pouze root. O funkčnosti zařízení se přesvědčíme malým testovacím programem:

#include 
int main(int argc, char** argv)
{
int fd = open("mydevice", 0);
if (fd == -1) {
perror("Nelze otevrit");
return 1;
}
if (close(fd) != 0) {
perror("Nelze zavrit");
return 2;
}
printf("Vsechno OK\n");
return 0;
}

Pokud je všechno v pořádku, program skončí s hlášením „Vsechno OK“. V opačném případě se (z chybové zprávy) hned dozvíme, v čem je problém (soubor nenalezen, přístup odepřen apod.). Podíváme-li se do výpisu jádra (dmesg), vidíme, co všechno se provedlo (tedy otevření a zavření souboru). Také bychom tam viděli i případné chybové zprávy. V nejhorším případě počítač zatuhne – sice jen v případě hrubé chyby v implementaci, ale je potřeba s tím počítat. Než se pustíme do implementace nějakých operací s modulem, je důležité vědět, jak pracovat s pamětí. Tedy jak paměť adresovat, dynamicky alokovat a uvolňovat, jaká pravidla je potřeba dodržet, čeho se vyvarovat atd. A na to se podíváme příště.

.

Nahoru

Odkazy

Příspěvky

Vývoj jádra IV. - základní operace
Petr Dvořák 5. 03. 2008, 07:41:17
Odpovědět  Odkaz 
V příkladu je chyba a v odregistrování char device má být jako první parametr struktura dev_t, nikoli jen její část MINOR(dev_t). Takto by jádro odregistrovalo zařízení s MAJOR číslem udaným chybně jako MINOR. Tedy místo například 252 by se snažilo odregistrovat 0 a podobně.

chybně:
unregister_chrdev_region(MINOR(s_dev0), MINOR_COUNT);

dobře:
unregister_chrdev_region(s_dev, MINOR_COUNT);

Přidat názor

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