Čo sa stane, keď vložíte do USB portu USB kľúč? Väčšinou nastáva komplikovaný proces od jadra cez systémových daemonov až ku užívateľským aplikáciám. Tento proces sa Vám pokúsim aspoň sčasti opísať.
Sysfs, netlink, d-bus
Na to, aby ste trochu pochopili, ako to celé pracuje, je nevyhnutné vysvetliť pár pojmov a mechanizmov, ktoré tvoria akési lepidlo medzi jednotlivými časťami.
Možno ste sa trochu pozastavili nad tým, prečo sú v jednej kategórii. Je to z toho dôvodu, že sa všetko jedná o spôsoby komunikácie, či už jadrom a užívateľským priestorom, alebo medzi dvoma aplikáciami v užívateľskom priestore.
Sysfs
je, podobne ako (možno známejší) proc špeciálnym filesystemom, ktorý v skutočnosti na disku vôbec neexistuje a jedná sa len o virtuálnu napodobeninu v RAM. Do sysfs jadro exportuje informácie o zariadeniach viditeľných systémom. Vždy (samozrejme, je to na programátorovi), keď sa objaví nové zariadenie, je v sysfs vytvorená položka (súbor, adresár), v ktorom sa nachádzajú informácie o zariadení, alebo je možnosť zápisom do súborov zariadenie dokonca konfigurovať. Tradične sa sysfs pripája do /sys. Kedže vývoj sysfs stále prebieha, skoro s každým jadrom sa mení, na manipuláciu s dátami sa používa knižnica libsysfs, ktorá sa aktualizuje vždy po zmene v jadre.
Netlink
poskytuje rozhranie na komunikáciu medzi jadrom a užívateľskými aplikáciami a aplikáciami medzi sebou. Využíva pritom sieťovanie, takže aplikácie komunikujú rovnako, ako keby komunikovali po sieti (nie až tak úplne rovnako, samozrejme sa nepožívajú TCP/IP protokoly).
D-bus
je ďalšie rozhranie pre komunikáciu medzi dvoma aplikáciami. Rozhranie zabezpečuje démon bežiaci v užívateľskom priestore. D-bus hojne využíva aj prostredie Gnome a KDE4. D-bus nájdete dnes na každom desktope.
Ako potom vyzerá celá spolupráca všetkých častí, vyjadruje nasledujúci obrázok.

Udev a HAL sú trochu detailnejšie popísané v nasledujúcich riadkoch.
Udev
Adresár /dev/
Každý, kto len pár hodín robí s Linuxom, sa zaiste stretol s adresárom /dev/. /dev/ obsahuje súbory zariadení (device files), prostredníctvom ktorých môže užívateľský program komunikovať so zariadením, ktoré súbor reprezentuje. Súbory zariadení sa rozdeľujú podľa spôsobu, akým pracujú s dátami a konkrétne sú to:
- Znakové - tieto zariadenia pracujú so vstupom po bytoch, t. j. klávesnica, myš, virtuálne terminály, ...
- Blokové - so vstupom pracujú po blokoch dát, to znamená, že nie je možné manipulovať s menším objemom dát ako s blokom (jeho veľkosť závisí od konkrétneho zariadenia) . Typicky sú to hardisky
$ ls -l /dev/hda1 /dev/dsp
crw-rw---- 1 root audio 14, 3 2009-11-30 15:48 /dev/dsp
brw-rw---- 1 root disk 3, 1 2009-11-30 15:48 /dev/hda1
Písmeno "c" na začiatku riadku signalizuje znakové zariadenie, zatiaľ čo "b" blokové. Z hore uvedeného výpisu je potom jasné, že /dev/dsp reprezentuje znakové zariadenie (v tomto prípade zvukovú kartu) a /dev/hda1 blokové (harddisk). Všetko ostatné, čo príkaz ls vypísal, je zabehnutému užívateľovi Linuxu jasné až na dvojicu čísel v strede. Prvé číslo v dvojici je Major číslo zariadenia, druhé Minor číslo. Podľa major čísla systém pri manipulácií so súborom zistí, ktorému ovládaču v jadre súbor prislúcha. Minor číslo potom len identifikuje konkrétne zariadenie vrámci kontextu ovládača. Demonštruje to nasledujúci výpis.
$ ls -l /dev/hd*
brw-rw---- 1 root disk 3, 0 2009-11-30 15:48 /dev/hda
brw-rw---- 1 root disk 3, 1 2009-11-30 15:48 /dev/hda1
brw-rw---- 1 root disk 3, 2 2009-11-30 15:48 /dev/hda2
brw-rw---- 1 root disk 3, 5 2009-11-30 15:48 /dev/hda5
brw-rw---- 1 root disk 3, 6 2009-11-30 15:48 /dev/hda6
brw-rw---- 1 root disk 3, 7 2009-11-30 15:48 /dev/hda7
brw-rw---- 1 root disk 3, 8 2009-11-30 15:48 /dev/hda8
brw-rw---- 1 root disk 3, 9 2009-11-30 15:48 /dev/hda9
Samotnému vytvoreniu súboru zariadenia predchádza registrácia zariadenia v jadre. Pri registrácii ovládač zariadenie pomenuje, alokuje major číslo, blok minor čísel a inicializuje súborové operácie. Je dôležité poznamenať, že názov zariadenia nie je to isté ako názov súboru v /dev/. Názov /dev/ môže byť a často aj je odlišný. Všetky zariadenia registrované ovládačmi v jadre sú zaznamenané v súbore /proc/devices vo forme názvov a major čísel. Skrátený výpis /proc/devices:
Character devices:
1 mem
4 /dev/vc/0
4 tty
4 ttyS
5 /dev/tty
5 /dev/console
5 /dev/ptmx
7 vcs
10 misc
13 input
14 sound
21 sg
37 ht
81 video4linux
89 i2c.....
Block devices:
1 ramdisk
3 ide0
259 blkext
7 loop
8 sd
Až potom, čo je zariadenie zaregistrované, má zmysel vytvárať súbory zariadení. Ale o tom už nižšie.
Statický /dev/ vs. devfs vs. udev
Po registrácií zariadenia sa musí vytvoriť súbor v /dev/. V dobách dávno minulých, keď svoju púť na desktop Linux iba začínal sa používal statický adresár /dev/. /dev/ sa proste naplnil množstvom súborov zariadení, pričom zariadenie, na ktoré ukazovali ani nemuselo existovať. Po objavení sa zariadenia v systéme, súbor už vytvorený bol (spolu s mnoho ďalšími nepotrebnými). Netreba zdôrazňovať drastické nevýhody takéhoto riešenia. Jednak nebolo možné zistiť aké zariadenia v systéme naozaj sú a adresár /dev/ obsahoval neprimerane veľa súborov (čo sa prejavilo napr. pri prehľadávaní). Dni statického /dev/ boli spočítané príchodom súborového systému devfs.
Devfs
(Device filesystem) je virtuálny filesystem (samozrejme mountpoint bol /dev/), ktorý riešil hlavné problémy statického /dev/. V prvom rade zariadenia boli dynamické, vytvárané v závislosti od toho, aké zariadenie sa v systéme skutočne nachádza. Tým bol samozrejme odstránený problém s obrovským /dev/ adresárom. Hlavné problémy devfs (kvôli ktorým bol neskôr aj odstránený) však bola integrácia v jadre a nedodržiavanie pravidiel a štandardov pri pomenovávaní súborov zariadení. Najväčším problémom bola kvalita kódu, čo s ohľadom na fakt, že devfs bol integrovaný v jadre spôsobovalo nemalé problémy. Ja sám som devfs nikdy nepoužíval, preto dúfam, že sa niekto skúsenejší vyjadrí dole v komentároch.
Udev
Vývojár Greg Kroah-Hartman potom prišiel s riešením, ktoré sa používa dnes na väčšine distribúcií (na všetkých?). Riešením bol systém udev, ktorý s prehľadom vyriešil všetky problémy devfs a statického /dev/. udev beží v podobe daemona (väčšinou /sbin/udevd), ktorý príjíma správy (uevents) z jadra a na základe nich potom vytvára súbory zariadení. Celý proces vyzerá nasledovne:
- Pri pripojení zariadenia ovládač v jadre vytvorí záznamy o zariadení v sysfs (v skutočnosti je to trochu komplikovanejšie)
- Ovládač vygeneruje uevent, ktorá je poslaná pomocou netlinku do užívateľského priestoru
- Udev zachytí uevent a na základe svojej konfigurácie vytvorí súbor zariadenia (pričom čerpá informácie zo sysfs)
- Udev informuje pomocou systému d-bus procesy, ktoré o to majú záujem (napr. hald)
Výhody udev sú jasné, presúva správu adresára /dev/ z kernelu do užívateľského priestoru, dodržiavanie štandardov a možnosť ľubovoľne pomenovať súbory zariadení, poskytuje informácie o manipulácií zo súbormi ostatným programom.
Konfigurácia udev
Konfiguračné súbory udev sú uložené v /etc/udev/ a /lib/udev/. Prvý súbor, do ktorého sa udev pozerá je /etc/udev/udev.conf. Mnoho toho nastaviť nejde, vlastne len tri položky. Prvou je udev_root, udáva zložku, kde s budú vytvárať súbory zariadení (deafaultne /dev/), druhá je udev_rules, ktorá udáva adresár s pravidlami (defaultný je /etc/udev/rules.d/) a konečne udev_log určuje s akou prioritou bude udev posielať správy démonovy syslog (tento démon sa stará o logovanie udalostí v systéme). Môže nadobudnúť hodnoty err, info a debug. V konfiguračnom súbore sa potom hodnoty nastavujú uvedením mena a za rovná sa hodnoty. Teda napr.
udev_log="info"
Potom, čo udev prečíta udev.conf začne prechádzať adresáre s pravidlami. Prechádza /lib/udev/rules.d/ a /etc/udev/rules.d/ (ak bol pomocou udev_rules definovaný iný adresár tak samozrejme ten). V /lib/udev/rules.d/ sa nachádzajú pravidlá vytvorené samotnou distribúciou, zatiaľ čo druhý menovaný obsahuje pravidlá doplnené užívateľom resp. užívateľskou aplikáciou. Pravidlá sú súbory s koncovkou .rules. Potom čo udev prejde všetky adresáre s pravidlami všetky súbory obsahujúce pravidlá abecedne zoradí (bez ohľadu na to, v ktorej zložke sa nachádzali). Preto sa súbory s pravidlami zväčša začínajú číslicami. V abecednom poradí pravidlá potom aj spracúva.
V prípade, že sa vyskytnú dva súbory s rovnakým menom, má prednosť ten v /etc/udev/rules.d/.
Písanie pravidiel
Písanie pravidiel je komplexná záležitosť preto sa jej nebudeme venovať úplne do hĺbky. Popíšeme si len základy, pre bližšie štúdium vás odkazujem odkazy uvedené na konci článku.
Každé pravidlo pozostáva z viacerých výrazov "KĽÚČ operátor HODNOTA" oddelených čiarkami (jednotlivé pravidlá sú oddelené novým riadkom). Kľúč môže mať buď priraďovací alebo porovnávací charakter. Porovnávacie kľúče sa používajú na testovanie nejakej podmienky (napr. mena virtuálneho zariadenia). Potom, čo sú vyhodnotené všetky porovnávacie výrazy ako pravdivé sú spracované priraďovacie výrazy (napr. priradenie mena v /dev/). V závislosti od kľúča treba primerane zvoliť operátor. Napr. porovnávací operátor (==) nemôžete použiť s priraďovacím kľúčom. Každé pravidlo by malo obsahovať aspoň jeden priraďovací a jeden porovnávací výraz, inač by veľmi zmysel nemalo.
Príklad veľmi jednoduchého pravidla:
KERNEL=="hdb",
Do slovenčiny môžme toto pravidlo prepísať takto: "Ak je meno zariadenia pridelené kernelom hdb, pomenuj súbor zariadenia /dev/moj_druhy_disk". KERNEL tu plní úlohu porovnávacieho pravidla a pomocou operátoru == testujeme či meno pridelené ovládačom v kerneli je hdb. Ak to tak je, pomocou kľúča NAME povieme udev-u aké meno má mať súboru zariadenia v /dev/. V tomto prípade moj_druhy_disk.
Udev samozrejme podporuje mnoho kľúčov. Nie je v mojej moci vymenovať všetky, ani väčšinu, preto si stručne popíšeme pár základných + niekoľko príkladov.
Poznámka: niektoré pravidlá (napr. NAME) môžu byť použité aj ako porovnávacie aj ako priraďovacie.
Porovnávacie
- KERNEL - meno zariadenia pridelené ovládačom v kerneli
- DRIVER - meno ovládača obsluhujúci zariadenia
- SUBSYSTEM - subsystém v ktorom sa zariadenie nachádza
- ATTR - kľúčom ATTR môžeme adresovať jednotlivé položky v sysfs. Keď chceme adresovať povedzme veľkosť disku, použijeme ATTR{size}
Priraďovacie
- NAME - meno zariadenia v /dev/
- SYMLINK - symbolický odkaz na zariadenie
KERNEL=="hda8", SYMLINK+="archlinux"
Toto pravidlo vložíme napr. do /etc/udev/rules.d/10-mojepravidlo.rules. Teraz budeme mať v /dev/archlinux symbolický odkaz na /dev/hda8 a nemusíme si tak pamätať meno súboru zariadenia. Ak sa pokúsite takýto symbolický odkaz vytvoriť ručne, pri najbližšom reštarte bude zmazaný.
Program udevadm
Pomocou programu udevadm spolu s parametrom info si môžeme uľahčiť tvorbu pravidiel. Nebudeme sa týmto programom zaoberať do hĺbky, od toho sú tu manuálové stránky. udevadm dokáže pristupovať k aktuálnej databáze udev a vypisovať získané údaje. Všetko povie príklad.
bash# udevadm info -a -p `udevadm info -q path -n /dev/hda8`
Udevadm info starts with the device specified by the devpath and then
walks up the chain of parent devices. It prints for every device
found, all possible attributes in the udev rules key format.
A rule to match, can be composed by the attributes of the device
and the attributes from one single parent device.
looking at device '/devices/pci0000:00/0000:00:0f.1/ide0/0.0/block/hda/hda8':
KERNEL=="hda8"
SUBSYSTEM=="block"
DRIVER==""
ATTR{partition}=="8"
ATTR{start}=="127957788"
ATTR{size}=="27776322"
ATTR{alignment_offset}=="0"
ATTR{stat}==" 5954 236 407754 82164 1659 3685 42768 19310 0 46361 101460"
looking at parent device '/devices/pci0000:00/0000:00:0f.1/ide0/0.0/block/hda':
KERNELS=="hda"
SUBSYSTEMS=="block"
DRIVERS==""
ATTRS{range}=="64"
ATTRS{ext_range}=="256"
ATTRS{removable}=="0"
ATTRS{ro}=="0"
ATTRS{size}=="156301488"
ATTRS{alignment_offset}=="0"
ATTRS{capability}=="50"
ATTRS{stat}==" 14790 7007 617635 188029 9246 10280 156138 242936 0 86467 430932"
looking at parent device '/devices/pci0000:00/0000:00:0f.1/ide0/0.0':
KERNELS=="0.0"
SUBSYSTEMS=="ide"
DRIVERS=="ide-gd"
ATTRS{media}=="disk"
ATTRS{drivename}=="hda"
ATTRS{modalias}=="ide:m-disk"
ATTRS{model}=="MAXTOR STM3802110A"
ATTRS{firmware}=="3.AAK"
ATTRS{serial}=="5LRAY3P7"
looking at parent device '/devices/pci0000:00/0000:00:0f.1/ide0':
KERNELS=="ide0"
SUBSYSTEMS==""
DRIVERS==""
looking at parent device '/devices/pci0000:00/0000:00:0f.1':
KERNELS=="0000:00:0f.1"
SUBSYSTEMS=="pci"
DRIVERS=="VIA_IDE"
ATTRS{vendor}=="0x1106"
ATTRS{device}=="0x0571"
ATTRS{subsystem_vendor}=="0x1043"
ATTRS{subsystem_device}=="0x81b5"
ATTRS{class}=="0x01018a"
ATTRS{irq}=="0"
ATTRS{local_cpus}=="ff"
ATTRS{local_cpulist}=="0-7"
ATTRS{modalias}=="pci:v00001106d00000571sv00001043sd000081B5bc01sc01i8a"
ATTRS{enable}=="1"
ATTRS{broken_parity_status}=="0"
ATTRS{msi_bus}==""
looking at parent device '/devices/pci0000:00':
KERNELS=="pci0000:00"
SUBSYSTEMS==""
DRIVERS==""
Tento príkaz nám vypísal všetky porovnávacie kľúče, pomocou ktorých môžeme identifikovať zariadenie v pravidlách. Napr. usb kľúč bude lepšie identifikovať nejakým jednoznačnejším kľúčom (napr. ATTRS{serial}) ako pomocou KERNEL, kedže je pravdepodobné, že usb kľúč bude mať meniace sa meno (sdb, sdc, ...) v závislosti od toho v ktorom poradí ho pripojíme.
Napr., máme obľúbený usb kľúč, ale keďže máme vždy pripojených niekoľko iných kľúčov, ktoré striedame nikdy nevieme ktorý v /dev/ je ten náš obľúbený. Vytvoríme si teda symbolický odkaz /dev/mojkluc, ktorý bude vždy ukazovať na zariadenie nášho usb kľúča.
Poznámka: user-friendly distribúcie to robia za vás, ale ak máte archlinux alebo slackware tak vám to dokáže uľahčiť život.
Nemôžeme použiť kľúč KERNEL, lebo nie je isté, že sa usb vždy bude volať rovnako. Zistíme si teda pomocou nástroje udevadm
aké kľúče môžme použiť a vyberieme ten najjednoznačnejší. Ja použijem ATTRS{serial}, keďže ten by mal byť pre každý kľúč jedinečný. udevadm mi teda vypísal
ATTRS{serial}=="CCCBB999988887777"
Zostaviť pravidlo je už teraz jednoduché:
ATTRS{serial}=="CCCBB999988887777", SYMLINK+="mojkluc"
a hotovo :). Teraz môžme používať /dev/mojkluc namiesto zdĺhavého zisťovania, ktorý súbor /dev/sda patrí nášmu kľúču.
Hal
V praxi však nestačí len vytvoriť súbor zariadenia. Treba načítať ovládač pre konkrétne zariadenie, prípadne pripojiť súborový systém (automount), vytvoriť ikonku a pod.. O toto sa v moderných distribúciách stará HAL (bežiaci v podobe systémového daemona). HAL zároveň udržuje databázu pripojených zariadení, ktorú poskytuje iným aplikáciám.
Každé zariadenie pripojené v systéme má v HAL svoj objekt, ktorý je identifikovaný pomocou UDI (Unique Device Identifier - Unikátny identifikátor zariadenia). Všetky objekty spravované HAL-om a ich vlastnosti je možné vypísať príkazom lshal. Príklad skráteného výpisu objektu reprezentujúceho jednu partíciu na disku:
udi = '/org/freedesktop/Hal/devices/volume_uuid_dabbb471_21de_4503_a4c9_3f48292d3d6b'
block.device = '/dev/hda8' (string)
block.is_volume = true (bool)
block.major = 3 (0x3) (int)
block.minor = 8 (0x8) (int)
block.storage_device =
'/org/freedesktop/Hal/devices/storage_serial_MAXTOR_STM3802110A_5LRAY3P7' (string)
info.capabilities = {'volume', 'block'} (string list)
info.category = 'volume' (string)
info.interfaces = {'org.freedesktop.Hal.Device.Volume'} (string list)
info.parent = '/org/freedesktop/Hal/devices/storage_serial_MAXTOR_STM3802110A_5LRAY3P7' (string)
info.product = 'Volume (ext3)' (string)
info.udi = '/org/freedesktop/Hal/devices/volume_uuid_dabbb471_21de_4503_a4c9_3f48292d3d6b' (string)
linux.sysfs_path = '/sys/devices/pci0000:00/0000:00:0f.1/ide0/0.0/block/hda/hda8' (string)
org.freedesktop.Hal.Device.Volume.method_argnames = {'mount_point fstype extra_options', 'extra_options', 'extra_options'} (string list)
org.freedesktop.Hal.Device.Volume.method_execpaths = {'hal-storage-mount','hal-storage-unmount', 'hal-storage-eject'} (string list)
org.freedesktop.Hal.Device.Volume.method_names = {'Mount', 'Unmount', 'Eject'} (string list)
org.freedesktop.Hal.Device.Volume.method_signatures = {'ssas', 'as', 'as'} (string list)
volume.block_size = 512 (0x200) (int)
volume.fstype = 'ext3' (string)
volume.fsusage = 'filesystem' (string)
volume.fsversion = '1.0' (string)
volume.ignore = false (bool)
volume.is_disc = false (bool)
volume.is_mounted = true (bool)
volume.is_mounted_read_only = false (bool)
volume.is_partition = true (bool)
volume.label = '' (string)
volume.linux.is_device_mapper = false (bool)
volume.mount.valid_options = {'ro', 'sync', 'dirsync', 'noatime', 'nodiratime', 'noexec', 'quiet', 'remount', 'exec', 'acl', 'user_xattr', 'data='}
(string list)
volume.mount_point = '/' (string)
...
...
Z výpisu môžeme vyčítať napr. miesto súboru zariadenia, jeho major a minor čísla, filesystem, či je disk pripojený, kde je pripojený a mnoho ďalšieho. Samozrejme tieto informácie sa dajú získať aj jednoduchším spôsobom.
Konfigurácia
Konfiguračné súbory HAL-u sa nachádzaujú v /usr/share/hal/fdi/ a /etc/hal/fdi/. Sú to súbory s príponou fdi vo formáte XML. Podobne ako u udev sa súbory spracuvávajú v abecednom poradí. Konfiguráciou halu sa zaoberať nebudeme (prečo? lebo sám v tom nemám prax, a užívateľ to nevyužije, pokiaľ napr. nevytvára vlastnú distribúciu).
Záver
Moderné distribúcie rozhodne správu hardware neriešia jednoducho. Systémy ako HAL tak trochu narušujú UNIX-ovú tradíciu a komplikujú správu systému. Je to však len dôsledok "zuserfriendliovania" linuxu, pretože bežný užívateľ si asi tažko bude pripájať disky a načítavať moduly ručne. Tento článok len povrchne prešiel základné mechanizmy a pritom je na webové pomery dlhý jak šľak. Zostáva len dúfať, že takéto monštrá čoskoro zaniknú a o pár rokov budú fuč :).
Zdroje:
http://www.kernel.org/pub/linux/utils/kernel/hotplug/udev.html
http://wikipedia.org/
http://www.reactivated.net/writing_udev_rules.htm






