Verwalten von Hardwareprioritäten

Die IRQL, bei der eine Treiberroutine ausgeführt wird, bestimmt, welche Kernelmodustreiberunterstützungsroutinen aufgerufen werden können. Beispielsweise erfordern einige Treiberunterstützungsroutinen, dass der Aufrufer unter IRQL = DISPATCH_LEVEL ausgeführt wird. Andere können nicht sicher aufgerufen werden, wenn der Aufrufer mit einem IRQL ausgeführt wird, der höher als PASSIVE_LEVEL ist.

Es folgt eine Liste der IRQLs, bei denen die am häufigsten implementierten Standardtreiberroutinen aufgerufen werden. Die IRQLs werden von der niedrigsten bis zur höchsten Priorität aufgeführt.

PASSIVE_LEVEL
Interrupts Masked Off – None.

Treiberroutinen, die unter aufgerufen werden PASSIVE_LEVEL – DriverEntry, AddDevice, Reinitialize, Unload Routines, die meisten Dispatchroutinen, vom Treiber erstellten Threads, Workerthreadrückrufe.

APC_LEVEL
Interrupts Masked Off – APC_LEVEL Interrupts werden maskiert.

Treiberroutinen, die unter aufgerufen werden APC_LEVEL – Einige Dispatchroutinen (siehe Dispatchroutinen und IRQLs).

DISPATCH_LEVEL
Interrupts Masked Off – DISPATCH_LEVEL und APC_LEVEL Interrupts werden maskiert. Geräte-, Uhr- und Stromausfallunterbrechungen können auftreten.

Treiberroutinen, die unter aufgerufen werden DISPATCH_LEVEL : StartIo, AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (während die Abbruch-Spin-Sperre gedrückt wird), DpcForIsr, CustomTimerDpc, CustomDpc Routinen.

DIRQL
Interrupts Masked Off – Alle Interrupts bei IRQL <= DIRQL des Interruptobjekts des Treibers. Geräteunterbrechungen mit einem höheren DIRQL-Wert können zusammen mit Unterbrechungen von Uhr und Stromausfall auftreten.

Treiberroutinen, die unter DIRQL – InterruptService, SynchCritSection-Routinen aufgerufen werden.

Der einzige Unterschied zwischen APC_LEVEL und PASSIVE_LEVEL besteht darin, dass ein Prozess, der bei APC_LEVEL ausgeführt wird, keine APC-Interrupts abrufen kann. Beide IRQLs implizieren jedoch einen Threadkontext, und beide impliziert, dass der Code ausgelagert werden kann.

Treiber der niedrigsten Ebene verarbeiten IRPs, während sie mit einer von drei IRQLs ausgeführt werden:

  • PASSIVE_LEVEL, ohne dass Unterbrechungen auf dem Prozessor maskiert sind, in den Dispatch-Routinen des Fahrers

    DriverEntry-, AddDevice-, Reinitialize- und Unload-Routinen werden ebenso wie alle vom Treiber erstellten Systemthreads auf PASSIVE_LEVEL ausgeführt.

  • DISPATCH_LEVEL mit DISPATCH_LEVEL- und APC_LEVEL-Unterbrechungen, die auf dem Prozessor maskiert sind, in der StartIo-Routine

    Die Routinen AdapterControl, AdapterListControl, ControllerControl, IoTimer, Cancel (während es die Abbruch-Spin-Sperre enthält) und CustomTimerDpc werden ebenfalls auf DISPATCH_LEVEL ausgeführt, ebenso wie DpcForIsr- und CustomDpc-Routinen.

  • Geräte-IRQL (DIRQL), wobei alle Interrupts kleiner oder gleich der SynchronizeIrql der Interruptobjekte des Treibers auf dem Prozessor maskiert sind, in den ISR- und SynchCritSection-Routinen

Die meisten Treiber auf höherer Ebene verarbeiten IRPs, während sie mit einer der beiden IRQLs ausgeführt werden:

  • PASSIVE_LEVEL, ohne dass Unterbrechungen auf dem Prozessor maskiert sind, in den Dispatchroutinen des Fahrers

    DriverEntry-, Reinitialize-, AddDevice- und Unload-Routinen werden ebenfalls auf PASSIVE_LEVEL ausgeführt, ebenso wie alle vom Treiber erstellten Systemthreads, Workerthreadrückrufroutinen oder Dateisystemtreiber.

  • DISPATCH_LEVEL mit DISPATCH_LEVEL- und APC_LEVEL-Unterbrechungen, die auf dem Prozessor maskiert sind, in den IoCompletion-Routinen des Treibers

    IoTimer-, Cancel- und CustomTimerDpc-Routinen werden ebenfalls DISPATCH_LEVEL ausgeführt.

Unter bestimmten Umständen werden die Treiber der mittleren und niedrigsten Ebene von Massenspeichergeräten am IRQL-APC_LEVEL aufgerufen. Dies kann insbesondere bei einem Seitenfehler auftreten, für den ein Dateisystemtreiber eine IRP_MJ_READ-Anforderung an niedrigere Treiber sendet.

Die meisten Standardtreiberroutinen werden mit einem IRQL ausgeführt, mit dem sie einfach die entsprechenden Supportroutinen aufrufen können. Ein Gerätetreiber muss beispielsweise AllocateAdapterChannel aufrufen, während er am IRQL-DISPATCH_LEVEL ausgeführt wird. Da die meisten Gerätetreiber diese Routinen aus einer StartIo-Routine aufrufen, werden sie in der Regel bereits mit DISPATCH_LEVEL ausgeführt.

Beachten Sie, dass ein Gerätetreiber, der keine StartIo-Routine hat, da er seine eigenen IRPs-Warteschlangen einrichtet und verwaltet, nicht unbedingt auf DISPATCH_LEVEL IRQL ausgeführt wird, wenn er AllocateAdapterChannel aufrufen sollte. Ein solcher Treiber muss seinen Aufruf von AllocateAdapterChannel zwischen Aufrufen von KeRaiseIrql und KeLowerIrql verschachteln, damit er beim Aufrufen von AllocateAdapterChannel am erforderlichen IRQL ausgeführt wird und die ursprüngliche IRQL wiederhergestellt wird, wenn die Aufrufroutine wieder die Kontrolle erhält.

Beachten Sie beim Aufrufen von Treiberunterstützungsroutinen Folgendes.

  • Das Aufrufen von KeRaiseIrql mit einem NewIrql-Eingabewert , der kleiner als der aktuelle IRQL ist, verursacht einen schwerwiegenden Fehler. Der Aufruf von KeLowerIrql mit Ausnahme der Wiederherstellung des ursprünglichen IRQL (d. h. nach einem Aufruf von KeRaiseIrql) verursacht ebenfalls einen schwerwiegenden Fehler.

  • Während der Ausführung unter IRQL >= DISPATCH_LEVEL verursacht der Aufruf von KeWaitForSingleObject oder KeWaitForMultipleObjects für kerneldefinierte Dispatcherobjekte, die auf ein Intervall ungleich null warten, einen schwerwiegenden Fehler.

  • Die einzigen Treiberroutinen, die sicher darauf warten können, dass Ereignisse, Semaphore, Mutexe oder Timer auf den signalierten Zustand festgelegt werden, sind Solche, die in einem nichtarbiträren Threadkontext bei IRQL PASSIVE_LEVEL ausgeführt werden, z. B. vom Treiber erstellte Threads, driverEntry - und Reinitialisierungsroutinen oder Dispatchroutinen für inhärent synchrone E/A-Vorgänge (z. B. die meisten Geräte-E/A-Steuerungsanforderungen).

  • Selbst während der Ausführung in IRQL PASSIVE_LEVEL darf der ausgelagerte Treibercode nicht KeSetEvent, KeReleaseSemaphore oder KeReleaseMutex aufrufen, wobei der Eingabeparameter Wait auf TRUE festgelegt ist. Ein solcher Aufruf kann zu einem schwerwiegenden Seitenfehler führen.

  • Jede Routine, die mit mehr als IRQL APC_LEVEL ausgeführt wird, kann weder Arbeitsspeicher aus einem ausgelagerten Pool zuweisen noch sicher auf Den Arbeitsspeicher im ausgelagerten Pool zugreifen. Wenn eine Routine, die bei IRQL ausgeführt wird, größer als APC_LEVEL einen Seitenfehler verursacht, handelt es sich um einen schwerwiegenden Fehler.

  • Ein Treiber muss unter IRQL DISPATCH_LEVEL ausgeführt werden, wenn Er KeAcquireSpinLockAtDpcLevel und KeReleaseSpinLockFromDpcLevel aufruft.

    Ein Treiber kann unter IRQL <= DISPATCH_LEVEL ausgeführt werden, wenn Er KeAcquireSpinLock aufruft, aber er muss diese Spin-Sperre durch Aufrufen von KeReleaseSpinLock freigeben. Mit anderen Worten, es ist ein Programmierfehler, eine mit KeAcquireSpinLock erworbene Drehsperre durch Aufrufen von KeReleaseSpinLockFromDpcLevel freizugeben.

    Ein Treiber darf KeAcquireSpinLockAtDpcLevel, KeReleaseSpinLockFromDpcLevel, KeAcquireSpinLock oder KeReleaseSpinLock nicht aufrufen, während er am IRQL-DISPATCH_LEVEL > ausgeführt wird.

  • Beim Aufrufen einer Supportroutine, die eine Drehsperre verwendet, z. B. eine ExInterlockedXxx-Routine , wird IRQL auf dem aktuellen Prozessor entweder auf DISPATCH_LEVEL oder auf DIRQL ausgelöst, wenn der Aufrufer nicht bereits bei einem ausgelösten IRQL ausgeführt wird.

  • Treibercode, der bei IRQL > PASSIVE_LEVEL ausgeführt wird, sollte so schnell wie möglich ausgeführt werden. Je höher der IRQL ist, bei dem eine Routine ausgeführt wird, desto wichtiger ist es für eine gute Gesamtleistung, diese Routine so schnell wie möglich auszuführen. Beispielsweise sollte jeder Treiber, der KeRaiseIrql aufruft , den wechselseitigen Aufruf an KeLowerIrql so bald wie möglich durchführen.

Weitere Informationen zum Festlegen von Prioritäten finden Sie im Whitepaper Planung, Threadkontext und IRQL .