Sdílet prostřednictvím


Úvod do objektů Mutex

Jak naznačuje název, objekt mutex je synchronizační mechanismus navržený tak, aby zajistil vzájemně se vylučující přístup k jednomu prostředku, který je sdílen mezi sadou vláken režimu jádra. Pouze ovladače nejvyšší úrovně, jako jsou ovladače systému souborů (FSD), které používají výkonná pracovní vlákna, pravděpodobně použijí objekt mutex.

Ovladač nejvyšší úrovně s vlákny vytvořenými ovladači nebo rutinami zpětného volání pracovních vláken může použít objekt mutex. Jakýkoli ovladač s stránkovatelnými vlákny nebo rutinami zpětného volání pracovních vláken však musí spravovat získání, čekání a uvolnění jeho objektů mutex velmi pečlivě.

Objekty Mutex mají integrované funkce, které systémovým vláknům (pouze v režimu jádra) poskytují výhradní přístup ke sdíleným prostředkům na počítačích SMP bez rizika zablokování. Jádro přiřazuje vlastnictví mutexu k jednomu vláknu najednou.

Získání vlastnictví mutexu brání doručení normálních asynchronních volání procedur v režimu jádra (APC). Vlákno nebude přerušeno APC, pokud jádro nespustí softwarové přerušení úrovně APC ke spuštění speciálního APC jádra, například rutinu dokončení IRP správce vstupně-výstupních operací, která vrací výsledky žadatele o původní vstupně-výstupní operaci.

Vlákno může získat vlastnictví objektu mutex, který již vlastní (rekurzivní vlastnictví), ale rekurzivně získaný objekt mutex není nastaven na signalovaný stav, dokud vlákno zcela neuvolní jeho vlastnictví. Takové vlákno musí explicitně uvolnit mutex tolikrát, kolikrát jej získalo, než jiné vlákno může mutex získat.

Jádro nikdy nepovoluje, aby vlákno, které vlastní mutex, způsobilo přechod do uživatelského režimu, aniž by nejprve uvolnilo mutex a nastavilo ho do stavu signalizace. Pokud se jakékoli vlákno vytvořené službou FSD nebo ovladač, které vlastní mutex, pokusí vrátit řízení I/O manažeru před uvolněním vlastnictví mutexu, jádro shodí systém.

Každý ovladač, který používá objekt mutex, musí volat KeInitializeMutex jednou, než počká nebo uvolní příslušný objekt mutex. Následující obrázek znázorňuje, jak mohou dvě systémová vlákna používat objekt mutex.

diagram znázorňující čekání na mutexový objekt

Jak ukazuje předchozí obrázek, ovladač, který používá objekt mutex, musí poskytnout paměť pro objekt mutex, který musí být trvale přítomen v paměti. Ovladač může použít rozšíření zařízení objektu zařízení vytvořeného ovladačem, rozšíření řadiče , pokud používá objekt řadiče, nebo nestránkovaný fond přidělený ovladačem.

Když ovladač volá KeInitializeMutex (obvykle ve své rutině AddDevice), musí předat ukazatel na úložiště ovladače pro objekt mutex, který jádro inicializuje do stavu Signaled.

Jakmile se ovladač nejvyšší úrovně inicializuje, může spravovat vzájemně se vylučující přístup k nějakému sdílenému prostředku, jak je znázorněno na předchozím obrázku. Například odesílací rutiny ovladače pro přirozeně synchronní operace a vlákna můžou používat mutex k ochraně fronty vytvořené ovladačem pro IRP.

Protože KeInitializeMutexvždy nastaví počáteční stav objektu mutex na signalizované (jak ukazuje předchozí obrázek):

  1. Počáteční volání rutiny odeslání KeWaitForSingleObject s ukazatelem Mutex umístí aktuální vlákno okamžitě do připraveného stavu, udělí vláknu vlastnictví mutexu a resetuje stav mutexu na Nesignalizovaný. Jakmile se rutina odesílání obnoví, může bezpečně vložit IRP do fronty chráněné mutexem.

  2. Když druhé vlákno (další rutina odesílání, rutina zpětného volání pracovních vláken dodané ovladačem nebo vlákno vytvořené ovladačem) zavolá funkci KeWaitForSingleObject s ukazatelem na Mutex, druhé vlákno je uvedeno do stavu čekání.

  3. Když vyřizovací rutina dokončí řazení do fronty, jak je popsáno v kroku 1, volá KeReleaseMutex s ukazatelem Mutex a Boolean hodnotou Wait, která označuje, zda má v úmyslu volat KeWaitForSingleObject (nebo KeWaitForMutexObject) s Mutex, jakmile KeReleaseMutex předá kontrolu.

  4. Za předpokladu, že rutina odeslání vydala své vlastnictví mutexu v kroku 3 (Čekání nastaveno na FALSE), je mutex nastaven do stavu signalizace KeReleaseMutex. Mutex aktuálně nemá žádného vlastníka, takže jádro určuje, jestli na toto mutex čeká jiné vlákno. Pokud ano, jádro udělá druhé vlákno (viz krok 2) vlastníkem mutexu, případně zvýší prioritu vlákna na nejnižší hodnotu v rámci priorit v reálném čase, a změní jeho stav na připravenost.

  5. Jádro odešle druhé vlákno ke spuštění, jakmile bude k dispozici procesor: to znamená, že pokud žádné jiné vlákno s vyšší prioritou není aktuálně v připraveném stavu a neexistují žádné rutiny režimu jádra, které by se měly spouštět v prostředí IRQL vyšší. Druhé vlákno (rutina, která přidává IRP do fronty pro zpracování, zpětné volání pracovního vlákna ovladače, nebo vlákno vytvořené ovladačem, které odebírá IRP z fronty) teď může bezpečně přistupovat k frontě IRP chráněné pomocí mutexu, dokud nevolá KeReleaseMutex.

Pokud vlákno získá vlastnictví objektu mutex rekurzivně, musí toto vlákno explicitně volat KeReleaseMutex tolikrát, kolikrát čekalo na mutex, aby objekt mutex nastavil do signalovaného stavu. Pokud například vlákno volá KeWaitForSingleObject a pak KeWaitForMutexObject se stejným ukazatelem Mutex, musí zavolat KeReleaseMutex dvakrát, když získá mutex, aby se objekt mutex nastavil do signalovaného stavu.

Volání KeReleaseMutex s parametrem Wait nastaveným na TRUE označuje záměr volajícího okamžitě volat rutinu podpory KeWaitXxx při návratu z KeReleaseMutex.

Zvažte následující pokyny pro nastavení parametru Wait na KeReleaseMutex:

Rutina stránkovatelného vlákna nebo ovladače pracující na úrovni IRQL PASSIVE_LEVEL by nikdy neměla volat KeReleaseMutex s parametrem Wait nastaveným na TRUE. Takové volání způsobí kritickou chybu stránky, pokud dojde k vyřazení volajícího mezi voláním KeReleaseMutex a KeWaitXxxObjekt(s).

Jakákoli standardní rutina ovladače, která běží na úrovni IRQL vyšší než PASSIVE_LEVEL, nemůže čekat na nenulový interval na žádném dispečerském objektu, aniž by to způsobilo selhání systému. Taková rutina však může volat KeReleaseMutex, pokud při spuštění na úrovni IRQL, která je menší nebo rovna DISPATCH_LEVEL, vlastní mutex.

Souhrn seznamů IRQLs, na kterých se spouští rutiny standardních ovladačů, najdete v tématu Správa priorit hardwaru.