Řekneme si něco podrobností o systému přerušení na architektuře Intel x86 a jeho obsluze v systému Linux. Základní otázka zvídavého čtenáře byla, kde jsou přerušení, jejichž čísla v patřičném výpisu souboru interrupts chybí. Odpověď je na jednu stranu velmi jednoduchá: Při tisku se prochází pole všech možných přerušení a tisknou se ty, u nichž je platný (nenulový) ukazatel na strukturu action, která obsahuje informace o obsluze přerušení. Patřičná funkce se jmenuje get_irq_list a nalézá se v souboru arch/xxx/kernel/irq.c. Položka action pro nás zatím bude pro zjednodušení představovat pouze akci vykonanou po přijetí příslušného přerušení. Pokud není co vykonat, nikdo asi přerušení neobsluhuje a tudíž nemá cenu něco vypisovat (stejně není co).
Pokud někomu tohle vysvětlení stačí, nechť laskavě přeskočí pár následujících odstavců (až na ten poslední :). Pro ty ostatní pro lepší orientaci zopakuji původní výpis z pátého dílu o souboru interrupts:
CPU0 0: 460943 IO-APIC-edge timer 1: 21161 IO-APIC-edge keyboard 2: 0 XT-PIC cascade 9: 460757 IO-APIC-level nvidia 10: 0 IO-APIC-level ESS Solo1, eth0 12: 83236 IO-APIC-edge PS/2 Mouse 14: 9901 IO-APIC-edge ide0 15: 5 IO-APIC-edge ide1 NMI: 0
Na první pohled by tohle základní vysvětlení možná mohlo vypadat použitelně, jenže existuje několik ALE. S tím, co jsme si právě řekli, si vystačíme například při vysvětlování, proč chybí přerušení číslo 7, o kterém (například z bios setupu) vím, že obsluhuje paralelní port. Podporu paralelního portu nemám zakompilovanou napevno do jádra, protože paralelní port používám výjimečně. Proto je tohle přerušení skutečně volné. Pokud budu potřebovat třeba propojit dva počítače pomocí PLIP a zavedu modul pro paralelní port například takto:
modprobe parport_pc
s následujícím obsahem /etc/modules.conf
... #Paralell port module options alias parport_lowlevel parport_pc options parport_pc io=0x378 irq=7 ...
Tak dostanu do syslogu hlášení:
Aug 2 16:19:24 sirion kernel: 0x378: FIFO is 16 bytes ... 0x378: writeIntrThreshold is 8 ... 0x378: readIntrThreshold is 8 ... parport0: PC-style at 0x378 (0x778), irq 7, using FIFO [PCSPP,TRISTATE,COMPAT,ECP]
Během inicializace modulu se provede mimo jiné žádost o přidělení potřebného přerušení, přesněji IRQ. Pokud provedu nyní výpis obsahu souboru /proc/interrupts, přibude nový řádek (označený šipkou):
2: 0 XT-PIC cascade -> 7: 2 IO-APIC-edge parport0 9: 539350 IO-APIC-level nvidia
Pokud moduly odstraním (příkazem rmmod), řádek opět zmizí. Stejná situace bude u ovladačů pro USB rozhraní, které nejsou také momentálně nahrány. Ty ovšem využívají IRQ 12 spolu s PS/2 myší a tak řádek nepřibude, jen se doplní druhý ovladač pro IRQ 12.
Dále se budeme zabývat sériovými porty. Dle BIOSu (Setupu) mají přiřazeny IRQ 3 a 4. Ovladač sériového portu mám opět jako modul, takže ho zavedu pomocí příkazu modprobe:
modprobe serial
A patřičný výpis ze syslogu:
Aug 2 17:27:38 sirion kernel: Serial driver version 5.05a (2001-03-20) with MANY_PORTS SHARE_IRQ SERIAL_PCI enabled Aug 2 17:27:38 sirion kernel: ttyS00 at 0x03f8 (irq = 4) is a 16550A Aug 2 17:27:38 sirion kernel: ttyS01 at 0x02f8 (irq = 3) is a 16550A
Nyní by se dalo čekat, že bude situace stejná jako u paralelního portu. Kupodivu se však soubor interrupts vůbec nezmění. Po delším pátrání jsem přišel na to, že vlastní přerušení se alokují až v okamžiku otevření souborů /dev/ttyS[0|1]. Pokud tedy zadáme třeba jen cat /dev/ttyS0 & cat /dev/ttyS1, objeví se kýžené řádky:
2: 0 XT-PIC cascade -> 3: 1 IO-APIC-edge -> 4: 1 IO-APIC-edge 7: 2 IO-APIC-edge parport0 9: 253542 IO-APIC-level nvidia
K malému úžasu ovšem není přítomen název ovladače (poslední sloupec výpisu) a to i přes to, že funkce pro požadavek IRQ se zdá být volána správně (s hodnotou příslušného parametru „serial“). Přiznám se, že mi není úplně jasné, proč tomu tak je. Předává se (a uchovává se) sice jen hodnota ukazatele na řetězec, ale adresový prostor jádra by měl být jednotný. Možná je to proto, že je ovladač přeložen jako modul.
Jakmile soubory ttySx uzavřeme, uvolní se i příslušné IRQ. Stejně to funguje i u ovladače floppy mechaniky. Stačí dát mount /mnt/floppy máme další řádek:
4: 1 IO-APIC-edge -> 6: 4 IO-APIC-edge floppy 7: 2 IO-APIC-edge parport0
Doufám, že jste získali představu jak to funguje, a tak zbytek jen shrnu. Chybí nám už jen IRQ číslo 5, 8, 11 a 13. Teď už to není složité. IRQ 5 patří ovladači APIC – tedy řadiči přerušení. IRQ 8 je Real Time Clock a jeho absence je opět způsobena chybějícím kódem v jádře nebo modulem. Řádek uvedu v koncovém výpisu. IRQ 11 je přiřazeno pro RAID řadič HPT370, ten je ovšem v současné době nevyužit. Podle literatury patří IRQ 13 jednotce FPU, tohle řešení komunikace s procesorem se (předpokládám) používalo v době samostatného koprocesoru. Dnes je tedy buď nedostupné (rezervované), nebo volné k libovolnému využití.
Na závěr této konkrétní ukázky tedy celý výpis ještě jednou:
CPU0 0: 842363 IO-APIC-edge timer 1: 37642 IO-APIC-edge keyboard 2: 0 XT-PIC cascade 3: 1 IO-APIC-edge 4: 1 IO-APIC-edge 6: 6 IO-APIC-edge floppy 7: 2 IO-APIC-edge parport0 8: 0 IO-APIC-edge rtc 9: 486873 IO-APIC-level nvidia 10: 0 IO-APIC-level ESS Solo1, eth0 12: 50836 IO-APIC-edge PS/2 Mouse 14: 19831 IO-APIC-edge ide0 15: 5 IO-APIC-edge ide1 NMI: 0 ERR: 0
Teď bych ještě rád zúročil všechnu námahu vynaloženou při hledání přesné implementace obsluh přerušení v Linuxu na architektuře Intel x86 a přidám ještě odstaveček o konstrukci IDT.
Procesory Intel x86 v chráněném režimu uchovávají informace o obsluze přerušení v segmentu, jehož obsahem je IDT – Interupt Descriptor Table. To je tabulka vstupních bodů přerušení. Pokud se vyskytne libovolné přerušení, vezme se jako index do této tabulky a provede se obsluha dle obsahu položky v tabulce. Tabulka může být v (nejen v chráněném režimu) umístěna kdekoliv v paměti počítače (tedy nejen prvních 1024 bajtů) a odkazuje na ni registr IDTR. Ten je ovládán privilegovanými instrukcemi LIDT, SIDT Load IDT, Store IDT. Prvky tabulky jsou deskriptory – struktura určená pro popis dalších segmentů v paměti v tomto případě segmentů s kódem obsluhy přerušení. Existuje několik variant obsahu deskriptoru pro obsluhu přerušení, ale tím se zde nebudu zabývat. Velikost této tabulky může být až 256 popisovačů (deskriptorů). Připomenu, že existují čtyři druhy přerušení: externí – maskovatelné a nemaskovatelné a interní – výjimky a softwarové přerušení. Ty jsou naskládány do této tabulky takto:
Index IDL | využití |
---|---|
0×00-x01F |
systémové,rezervované přerušení. 0×00 – 0×13 |
0×20–0×2F |
hardwarová přerušení – IRQ. Mapování je nastaveno ve funkci |
0×30–0×EF |
Přerušení pro potřeby ovladačů. Taktéž maskovatelná. Přerušení 0×80 má |
0×F0–0×FF |
Konec tabulky je vyhrazen systémovým přerušením převážně pro SMP |
Softwarová přerušení jsou vyvolávána instrukcí INT jejímž parametrem je index do tabulky. Tímto způsobem lze tedy vyvolat libovolné z uvedených přerušení. Vyvolání instrukcí INT lze ovšem maskovat. Maximální počet uvažovaných přerušení (např. při výpisu souboru interrupts) je definován jako NR_IRQS a jeho hodnota je v současnosti 224 (16 IRQ + 208 APIC přerušení – adresový prostor 0×20–0×FF).
Příště už budeme pokračovat normálně souborem ksyms.