Špecifiká vývoja ovládačov 3
V minulom dieli Špecifiká vývoja ovládačov 2 sme sa zamerali na pamäť. V tomto diele miniseriálu o rôznych aspektoch vývoja ovládačov sa budeme zaoberať len jedným témou, ktoré prestupuje celým jadrom a každý vývojár ovládačov by s ním mal byť dobre oboznámený. Ide o mechanizmus hardvérových priorít.
Na rozdiel od plánovacie priority vlákien (a poťažmo procesov), hardvérová priorita vlákna neudáva, ako často mu bude pridelený procesor, ale aké úkony môže vykonávať. Pritom platí, že kód bežiaci s hardvérovou prioritou X môže byť prerušený (Preplánovanie) iba kódom s hardvérovou prioritou Y> X. Čím vyššia hardvérovú prioritou vlákno disponuje, tým menej vecí môže vykonávať, ale tým menšie množstvo udalostí môže prerušiť jeho beh. Termín hardvérová priorita je mnou vymyslený slovenský ekvivalent súslovie interrupt request level (IRQL), ktorá sa používa v dokumentácii.
Existuje 32 rôznych hardvérových priorít pre 32-bitový a 16 pre
64-Windows. Najdôležitejšie hodnoty sú 0 (PASSIVE_LEVEL
), 1
(APC_LEVEL
), 2 (DISPATCH_LEVEL
) a 31 (resp. 15)
(HIGH_LEVEL
). Tabuľka 1 popisuje aké obmedzenia tieto priority
kladú na vykonávaný kód.
Tabuľka 1: Prehľad najdôležitejších IRQL a ich obmedzenia
PASSIVE_LEVEL | APC_LEVEL | DISPATCH_LEVEL | HIGH_LEVEL | |
---|---|---|---|---|
pasívne čakanie | ÁNO | ÁNO | NIE | NIE |
aktívne čakania | ÁNO | ÁNO | ÁNO | ÁNO |
stránkovanie | ÁNO | ÁNO | NIE | NIE |
alokácia pamäte | ÁNO | ÁNO | ÁNO * | NIE |
API | ÁNO | NIE | NIE | NIE |
DISPATCH_LEVEL
vyššie veľmi obmedzuje možnosti, ktoré
vývojár ovládače na danej hardvérovej priorite má.
Pri Aktívnym čakaní vlákno beží na procesore a neustále testuje, či je určitá podmienka splnená či stále ešte nie. Keďže tento druh čakania nevyžaduje žiadnu podporu od operačného systému, je možné ho vykonávať na ľubovoľnej IRQL.
Ako bolo povedané v minulom dieli, stránkovanie môže byť systémom
odložená na disk a odtiaľ načítaná až v okamihu potreby. Keďže pre
takýto úkon je potreba na načítanie dát (pasívne) počkať, ku
stránkovanie nemožno pristupovať a ani ju (de) alokovať na IRQL> =
DISPATCH_LEVEL
.
Do oblasti nestránkovej pamäti (prípadne stránkovanie, ktoré bolo
zakázané odobrať sa na disk) je možné pristupovať na ľubovoľné HW
priorite. Jediné obmedzenie platí pre jej (de) alokáciu, tieto činnosti je
nutné vykonávať maximálne na APC_LEVEL
(DISPATCH_LEVEL
pre nestránkovú).
Na úrovni DISPATCH_LEVEL
je vykonávaná väčšina rutín
plánovača. Vďaka tomu je možné napríklad preplánovať vlákno bežiaci na
PASSIVE_LEVEL
iným vláknom na rovnakej hardvérovej priorite,
hoci to poučka uvedená vyššie zdanlivo zakazuje. Trik spočíva v tom, že
plánovač IRQL zvýši na DISPATCH_LEVEL
, vykoná vlastné
preplánovanie a následne ju zníži na úroveň novo naplánovaného vlákna.
Dočasné zvýšenie IRQL patrí medzi legitímne kroky, nedochádza teda k
porušeniu žiadneho jadrového zákona.
Obrázok nižšie ilustruje stav opísaný v predchádzajúcom odseku. Na
procesora najprv beží vlákno A
na IRQL APC_LEVEL
.
Po určitej dobe plánovač usúdi, že nastal čas na zmenu; IRQL sa zvýši na
DISPATCH_LEVEL
a dôjde k naplánovanie vlákna B
.
Plánovač potom svoju intervenciu ukončí a nechá bežať vlákno
B
. IRQL klesne na poslednú známu hodnotu pre vlákno
B
, čo je v tomto prípade PASSIVE_LEVEL
. Vďaka
dočasnému zvýšeniu na DISPATCH_LEVEL
nedošlo k porušeniu
žiadneho jadrového pravidla.
Z predchádzajúceho odseku tiež vyplýva, že ak vlákno zdvihne HW
prioritu nad APC_LEVEL
, nemôže ho z aktuálneho procesora nikto
"vyštípat". Môže byť krátkodobo prerušené inými udalosťami
nastávajúcimi na vyšších IRQL (hardvérová prerušenia, časovač,
meziprocesorová komunikácie ...), tie ho ale nemôžu preplánovať. Z tohto
dôvodu na vyšších IRQL nedáva príliš zmysel hovoriť o vláknach, ale len
o procesoroch.
Vzhľadom k vyššie popísaným obmedzením je cieľom každého ovládača
vykonávať maximum kódu na IRQL PASSIVE_LEVEL
(prinajhoršom na
APC_LEVEL
). Pre zostúpení z vyšších IRQL na nižšiu existujú
dokumentované postupy, ktorý spočívajú v tom, že ovládač naplánuje
operáciu, ktorú potrebuje vykonať na nízkej IRQL, na neskoršiu dobu a
vráti riadenie jadru systému. Na vykonanie operácie nemôže počkať, neb
služby plánovača nie sú na vyšších IRQL dostupné. Tieto mechanizmy sa
nazývajú odložené volanie procedúry a pracovné vlákna.
Odložené volanie procedúry (Deferred Procedure Call -
DPC) slúži k zníženiu hodnoty IRQL na DISPATCH_LEVEL
.
Najčastejšie použitie nájdeme v obslužných rutinách prerušenia.
Napríklad stlačenie klávesu na notebookové klávesnici vyvolá hardvérové
prerušenia, ktoré spracováva ovládač i8042prt.sys
. Jeho
obslužná rutina je, ako to u prerušenie býva, volaná na IRQL>
DISPATCH_LEVEL
, a tak sú jej možnosti veľmi obmedzené.
Ovládač teda iba zistí, k akej udalosti klávesnice došlo (vrátane toho,
aká klávesa bola stlačená) a požiada systém, aby, až IRQL dostatočne
klesne, zavolal určitú funkciu a odovzdal jej zistené informácie v
parametroch. Tým vlastne odloží spracovanie informácií na
vhodnejšie obdobie. Bezpochyby nie je bez zaujímavosti, že si ovládač
môže vybrať procesor, na ktorom má byť odložená procedúra vykonaná.
Ak pri spracovaní informácií ovládač usúdi, že úroveň
DISPATCH_LEVEL
je pre neho stále príliš vysoká, môže
požiadať o pomoc niektorej z tzv. Pracovných vlákien
(worker threads) - množiny vlákien bežiacich na IRQL
PASSIVE_LEVEL
a vykonávajúcich prácu, ktorú im zadá
ľubovoľná komponenta jadra. Práca v tomto prípade znamená volanie funkcie
so zadaným parametrom. Mechanizmus je teda navonok značne podobný DPC.
Akonáhle sa ovládač dostane na PASSIVE_LEVEL
, môže napríklad
vykonávať zápisy do registra či do súboru, čo sú operácie na vyšších
IRQL zakázané.
Systém sa ovládače pre vykonávanie maxima kódu na nízkych IRQL snaží donútiť aj tým, že obmedzuje časový interval, ktorý možno na vyšších úrovniach stráviť. Pokiaľ ho ovládač prekročí, dochádza k modrej obrazovke smrti. Vzhľadom na to, že ovládače sú vlastne DLL knižnice, ktoré systém zavolá, keď je potreba, splnenie tejto požiadavky zvyčajne nepredstavuje veľký problém - ovládač urobí, čo musí, a v prípade potreby použije mechanizmus pre zníženie IRQL a odovzdá konania späť volajúcemu.
Časový limit pre beh kódu na IRQL> =
DISPATCH_LEVEL
má svoj význam, pretože na procesoroch s týmto
IRQL nefunguje plánovač, čo narušuje ilúziu súčasného vykonávanie
mnohých úloh.