Sdílet prostřednictvím


Uzamčení stránkovatelného kódu nebo dat

Některé ovladače režimu jádra, jako jsou sériové a paralelní ovladače, nemusí být rezidentem paměti, pokud nejsou zařízení, která spravují, otevřená. Pokud ale existuje aktivní připojení nebo port, musí být část kódu ovladače, která tento port spravuje, rezidentem pro službu zařízení. Pokud se port nebo připojení nepoužívá, kód ovladače se nevyžaduje. Naproti tomu ovladač disku, který obsahuje systémový kód, kód aplikace nebo systémový stránkovací soubor, musí být vždy rezidentem paměti, protože ovladač neustále přenáší data mezi jeho zařízením a systémem.

Ovladač pro sporadicky používané zařízení (například modem) může uvolnit systémové místo, když zařízení, které spravuje, není aktivní. Pokud umístíte do jedné části kód, který musí být rezidentem pro obsluhu aktivního zařízení, a pokud ovladač uzamkne kód v paměti při použití zařízení, může být tento oddíl označen jako stránkovatelný. Po otevření zařízení ovladače operační systém přenese stránkovatelný oddíl do paměti a ovladač ho uzamkne, dokud ho už nepotřebujete.

Tento postup používá kód zvukového ovladače CD systému. Kód ovladače je seskupený do stránkovatelných oddílů podle výrobce zařízení CD. Některé značky nemusí být v daném systému nikdy přítomny. I když CD-ROM v systému existuje, může se k němu přistupovat jen zřídka, takže seskupování kódu do stránkovatelných oddílů podle typu CD zajistí, aby se kód pro zařízení, která na konkrétním počítači neexistují, nikdy nenačetl. Při přístupu k zařízení ale systém načte kód pro příslušné zařízení CD. Ovladač pak volá rutinu MmLockPagableCodeSection, jak je popsáno níže, aby během používání jeho zařízení zamkl svůj kód do paměti.

Pokud chcete izolovat stránkovatelný kód do pojmenovaného oddílu, označte ho následujícím direktivou kompilátoru:

#pragma alloc_text(PAGE*Xxx; *RoutineName)

Název stránkovatelného oddílu kódu musí začínat čtyřmi písmeny PAGE a může za ním následovat až čtyři znaky (zde reprezentované jako Xxx), aby bylo možné oddíl jednoznačně identifikovat. První čtyři písmena názvu oddílu (tj. PAGE) musí být velká písmena. RoutineName identifikuje vstupní bod, který se má zahrnout do stránkovatelné sekce.

Nejkratší platný název oddílu stránkovatelného kódu v souboru ovladače je jednoduše PAGE. Například direktiva pragma v následujícím příkladu kódu identifikuje RdrCreateConnection jako vstupní bod v oddílu kódu s možností stránky s názvem PAGE.

#ifdef  ALLOC_PRAGMA 
#pragma alloc_text(PAGE, RdrCreateConnection) 
#endif 

Aby byl kód ovladače nestránkovatelný a uzamčený v paměti, ovladač volá MmLockPagableCodeSection, předává adresu (obvykle vstupní bod rutiny ovladače), která je z oddílu stránkovatelného kódu. MmLockPagableCodeSection uzamkne celý obsah oddílu, který obsahuje rutinu odkazovanou ve volání. Jinými slovy, toto volání způsobí, že každá rutina přidružená ke stejnému identifikátoru PAGEXxx bude rezidentní a uzamčená v paměti.

MmLockPagableCodeSection vrátí popisovač, který se má použít při odemknutí oddílu (voláním rutiny MmUnlockPagableImageSection), nebo když ovladač musí uzamknout oddíl v jiných částech svého kódu.

Ovladač může také zacházet s zřídka používanými daty jako se stránkovatelnými daty, aby bylo možné je také stránkovat, dokud zařízení, které podporuje, není aktivní. Například ovladač systémového mixéru používá stránkovatelná data. Zařízení mixéru nemá přidružené žádné asynchronní vstupně-výstupní operace, takže tento ovladač může učinit data stránkovatelnými.

Název stránkovatelného datového oddílu musí začínat čtyřmi písmeny PAGE a může za ním následovat až čtyři znaky, aby bylo možné oddíl jednoznačně identifikovat. První čtyři písmena názvu oddílu (tj. PAGE) musí být velká písmena.

Vyhněte se přiřazování stejných názvů k oddílům kódu a dat. Aby byl zdrojový kód čitelnější, vývojáři ovladačů obvykle přiřazují název PAGE oddílu s kódem stránky, protože tento název je krátký a může se objevit v mnoha alloc_text direktivách pragma. Delší názvy se pak přiřazují k jakýmkoli stránkovatelným datovým oddílům (například PAGEDATA pro data_seg, PAGEBSS pro bss_seg atd.), které ovladač může vyžadovat.

Například první dvě direktivy pragma v následujícím příkladu kódu definují dva stránkovatelné datové oddíly, PAGEDATA a PAGEBSS. PAGEDATA je deklarován pomocí direktivy pragma data_seg a obsahuje inicializovaná data. FUNKCE PAGEBSS je deklarována pomocí direktivy pragma bss_seg a obsahuje neinicializovaná data.

#pragma data_seg("PAGEDATA")
#pragma bss_seg("PAGEBSS")

INT Variable1 = 1;
INT Variable2;

CHAR Array1[64*1024] = { 0 };
CHAR Array2[64*1024];

#pragma data_seg()
#pragma bss_seg()

V tomto příkladu Variable1 a Array1 v kódu jsou explicitně inicializovány a jsou proto umístěny v části PAGEDATA. Variable2 a Array2 jsou implicitně inicializovány nulou a jsou umístěny v části PAGEBSS.

Implicitně inicializace globálních proměnných na nulu zmenšuje velikost spustitelného souboru na disku a upřednostňuje se před explicitní inicializací na nulu. Explicitní inicializaci na nulu by se mělo vyvarovat, s výjimkou případů, kdy je to nutné pro umístění proměnné do konkrétního datového oddílu.

Aby byla datová sekce rezidentní v paměti a uzamčená v paměti, ovladač volá MmLockPagableDataSection a předává datovou položku, která se nachází v sekci stránkovatelných dat. MmLockPagableDataSection vrátí popisovač, který se použije při následném uzamčení nebo odemknutí požadavků.

K obnovení stránkovatelného stavu uzamčeného oddílu zavolejte MmUnlockPagableImageSection a předejte hodnotu popisovače vrácenou MmLockPagableCodeSection nebo MmLockPagableDataSection, podle potřeby. Rutina Unload ovladače musí volat MmUnlockPagableImageSection aby uvolnila každý popisovač, které si ovladač zajistil pro zamknutelný kód a datové oddíly.

Uzamčení oddílu je náročná operace, protože správce paměti musí před uzamčením stránek do paměti prohledávat seznam načtených modulů. Pokud ovladač uzamkne sekci z mnoha míst ve svém kódu, měl by po počátečním volání MmLockPagableXxxSection použít efektivnější MmLockPagableSectionByHandle.

Popisovač předaný do MmLockPagableSectionByHandle je popisovač vrácený předchozím voláním MmLockPagableCodeSection nebo MmLockPagableDataSection.

Správce paměti udržuje počet pro každý popisovač oddílu a zvýší tento počet pokaždé, když ovladač volá MmLockPagableXxx pro tento oddíl. Volání MmUnlockPagableImageSection dekrementuje počet. Dokud je čítač pro jakýkoli popisovač oddílu nenulový, tento oddíl zůstává uzamčený v paměti.

Popisovač oddílu je platný, pokud je načten jeho ovladač. Ovladač by proto měl volat MmLockPagableXxxSekce pouze jednou. Pokud ovladač vyžaduje další zamykací operace, měl by použít MmLockPagableSectionByHandle.

Pokud ovladač volá jakoukoli rutinu MmLockPagableXxx pro oddíl, který je již uzamčen, správce paměti zvýší počet odkazů pro oddíl. Pokud je oddíl stránkován při volání rutiny zámku, správce paměti načte oddíl do paměti a nastaví počet odkazů na jeden.

Použití této techniky minimalizuje vliv ovladače na systémové prostředky. Když se ovladač spustí, může se uzamknout do paměti kódu a dat, která musí být rezidentní. Pokud pro své zařízení nejsou žádné nevyřízené vstupně-výstupní požadavky (to znamená, když je zařízení zavřené nebo pokud se zařízení nikdy neotevřelo), může ovladač odemknout stejný kód nebo data a umožnit jejich přesunutí na disk.

Jakmile však ovladač připojí přerušení, musí být jakýkoli kód ovladače, který lze volat během zpracování přerušení, vždy rezidentní v paměti. Zatímco některé ovladače zařízení mohou být stránkovatelné nebo uzamčené do paměti na vyžádání, některá základní sada kódu ovladače a data musí být trvale rezidentní v systémovém prostoru.

Zvažte následující pokyny implementace pro uzamčení kódu nebo datového oddílu.

  • Primárním použitím rutin Mm(Un)LockXxx je umožnit normálně nestránkovaný kód nebo data, aby mohly být stránkovatelné a následně používány jako nestránkovaný kód nebo data. Dobrými příklady jsou ovladače, jako je sériový ovladač a paralelní ovladač: pokud zařízení, které daný ovladač spravuje, nemá otevřené žádné popisovače, části kódu nejsou potřeba a mohou zůstat stránkované. Přesměrovávač a server jsou také dobrými příklady ovladačů, které mohou tuto techniku používat. Pokud neexistují žádná aktivní připojení, je možné obě tyto komponenty odkládat do swapu.

  • Celý stránkovatelný oddíl je zamknutý do paměti.

  • Jedna část kódu a jedna pro data pro každý ovladač je efektivní. Mnohé pojmenované oddíly, které lze stránkovat, jsou obecně neefektivní.

  • Udržujte čistě stránkovatelné oddíly oddělené od částí, které jsou stránkované, ale lze je uzamknout na vyžádání.

  • Mějte na paměti, že MmLockPagableCodeSection a MmLockPagableDataSection by neměly být často volány. Tyto rutiny můžou způsobit těžkou vstupně-výstupní aktivitu, když správce paměti načte oddíl. Pokud ovladač musí uzamknout oddíl z několika míst kódu, měl by použít MmLockPagableSectionByHandle.