Einrichten und Verwenden von ineinander verschachtelten Warteschlangen
Neue Treiber sollten das abbruchsichere IRP-Warteschlangenframework vor den in diesem Abschnitt beschriebenen Methoden verwenden.
Treiber mit dedizierten Threads oder Treibern, die Leitende Workerthreads verwenden, z. B. die meisten System-FSDs, sind die wahrscheinlichsten Arten von Treibern, die ihre eigene interne Laufzeitwarteschlange von IRPs in einer ineinandergreifenden Warteschlange verwalten. Alle PnP-Treiber, einschließlich WDM-Treiber, müssen auch bestimmte IRPs intern in die Warteschlange stellen, während PnP- und Energiezustandsübergänge durchgeführt werden.
In der Regel richten diese Treiber eine doppelt verknüpfte ineinandergreifende Warteschlange ein. Jedes IRP enthält einen Member vom Typ LIST_ENTRY, den ein Treiber verwenden kann, um IRPs, die er derzeit enthält, doppelt zu verknüpfen. Ein Treiber kann IRPs für Wiederholungsversuche nicht erneut in die Warteschlange stellen, wenn er eine eng miteinander verknüpfte Warteschlange einrichtet.
Ein Treiber muss seine ineinandergreifene Warteschlange bei der Geräteinitialisierung einrichten. Die folgende Abbildung veranschaulicht eine doppelt verknüpfte ineinandergreifende Warteschlange, die Supportroutinen, die ein Treiber aufrufen muss, um eine solche Warteschlange einzurichten, und eine Reihe von ExInterlockedXxx-Routinen , die ein Treiber aufrufen kann, um IRPs in die Warteschlange einzufügen und aus der Warteschlange zu entfernen.
Wie in dieser Abbildung gezeigt, muss ein Treiber den Speicher für die Warteschlange selbst und für Folgendes bereitstellen, um eine doppelt verknüpfte ineinandergreifende Warteschlange einzurichten:
Eine Executive Spin-Sperre, die der Treiber zum Initialisieren keInitializeSpinLock aufrufen muss. In der Regel initialisiert ein Treiber die Spinsperre, wenn er die Geräteerweiterungen für seine Geräteobjekte in seiner AddDevice-Routine einrichtet .
Der Listenkopf für die Warteschlange, die der Treiber durch Aufrufen von InitializeListHead initialisieren muss.
Die meisten Treiber, die doppelt verknüpfte ineinandergreifende Warteschlangen verwenden, stellen den erforderlichen Speicher in der Geräteerweiterung eines vom Treiber erstellten Geräteobjekts bereit. Die Warteschlangen- und Executive-Spin-Sperre kann sich stattdessen in einer Controllererweiterung (wenn der Treiber ein Controllerobjekt verwendet) oder in einem vom Treiber zugewiesenen Nicht-Auslagerpool befinden.
Während der Treiber E/A-Anforderungen akzeptiert, kann er ein IRP in seine Warteschlange einfügen, indem er eine der folgenden Supportroutinen aufruft, wenn der ListHeadvom Typ LIST_ENTRY ist, wie in der vorherigen Abbildung gezeigt:
ExInterlockedInsertTailList , um das IRP am Ende der Warteschlange zu platzieren
ExInterlockedInsertHeadList , um das IRP vorne in der Warteschlange zu platzieren. Treiber rufen diese Routine in der Regel nur auf, wenn sie eine bestimmte Anforderung wiederholen müssen.
Der Treiber muss Zeiger an das IRP (ListEntry) sowie die Zuvor initialisierten ListHead - und Executive Spin Lock-Zeiger (Lock) an jede dieser ExInterlockedInsertXxxList-Routinen übergeben. Nur Zeiger auf ListHead und Lock sind erforderlich, wenn der Treiber eine IRP durch Aufrufen von ExInterlockedRemoveHeadList aus der Warteschlange entfernt. Um Deadlocks zu verhindern, darf der Treiber keinen ExecutiveSpinLock halten, den er an eine ExInterlockedXxx-Routine übergibt.
Da eine ineinandergreifende Warteschlange durch die Spin-Sperre der Führungskraft geschützt ist, kann der Treiber IRPs in seine doppelt verknüpfte Warteschlange einfügen und sie auf multiprozessorsichere Weise aus jeder Treiberroutine entfernen, die kleiner oder gleich IRQL = DISPATCH_LEVEL ausgeführt wird.
Eine Warteschlange mit einem ListHead vom Typ LIST_ENTRY, wie in der vorherigen Abbildung gezeigt, ist eine doppelt verknüpfte Liste. Eine mit einem ListHead vom Typ SLIST_HEADER ist eine sequenzierte, einzeln verknüpfte Liste. Ein Treiber initialisiert den ListHead für eine sequenzierte, singly verknüpfte ineinandergreifende Warteschlange durch Aufrufen von ExInitializeSListHead.
Ein Treiber, der E/A-Vorgänge nie wiederholt, kann ExInterlockedPushEntrySList und ExInterlockedPopEntrySList verwenden, um die Warteschlange von IRPs intern in einer sequenzierten, eng miteinander verknüpften Warteschlange zu verwalten. Jeder Treiber, der diesen Typ von ineinandergreifender Warteschlange verwendet, muss auch residenten Speicher für einen ListHead vom Typ SLIST_HEADER und für einen ExecutiveSpinLock bereitstellen, wie in der vorherigen Abbildung dargestellt. Es muss die Spinsperre initialisieren und seine Warteschlange einrichten, bevor ExInterlockedPushEntrySList aufgerufen wird, um den ersten Eintrag in die Warteschlange einzufügen.
Weitere Informationen finden Sie unter Verwalten von Hardwareprioritäten und Spinsperren. Informationen zu IRQL-Anforderungen für eine bestimmte Supportroutine finden Sie auf der Referenzseite der Routine.