Verwalten von verriegelten Warteschlangen mit einem Driver-Created Thread
Neue Treiber sollten das abbruchsichere IRP-Warteschlangenframework vor den in diesem Abschnitt beschriebenen Methoden verwenden.
Wie der System-Floppycontrollertreiber verwaltet ein Treiber mit einem dedizierten Gerätethread anstelle einer StartIo-Routine in der Regel seine eigenen Warteschlangen von IRPs in einer doppelt miteinander verknüpften verzahnten Warteschlange. Der Thread des Treibers ruft IRPs aus der ineinander verriegelten Warteschlange ab, wenn auf dem Gerät Noch-Arbeiten ausgeführt werden müssen.
Im Allgemeinen muss der Treiber die Synchronisierung mit seinem Thread mit allen Ressourcen verwalten, die zwischen dem Thread und anderen Treiberroutinen gemeinsam genutzt werden. Der Treiber muss auch eine Möglichkeit haben, seinen vom Treiber erstellten Thread zu benachrichtigen, dass IRPs in eine Warteschlange gestellt werden. In der Regel wartet der Thread auf einem Dispatcherobjekt, das in der Geräteerweiterung gespeichert ist, bis die Dispatch-Routinen des Treibers das Dispatcherobjekt auf den Signalzustand festlegen, nachdem ein IRP in die verriegelte Warteschlange eingefügt wurde.
Wenn die Dispatch-Routinen des Treibers aufgerufen werden, überprüft jede die Parameter im E/A-Stapelspeicherort des Eingabe-IRP und stellt die Anforderung für die weitere Verarbeitung in die Warteschlange. Für jede IRP, die in einem treiberspezifischen Thread in die Warteschlange gestellt wird, sollte die Dispatchroutine den Kontext einrichten, den ihr Thread für die Verarbeitung dieses IRP benötigt, bevor ExInterlockedInsertXxxList aufgerufen wird. Der E/A-Stapelspeicherort des Treibers in jedem IRP ermöglicht dem Thread des Treibers Zugriff auf die Geräteerweiterung des Zielgeräteobjekts, wo der Treiber Kontextinformationen für seinen Thread freigeben kann, da der Thread jede IRP aus der Warteschlange entfernt.
Ein Treiber, der abbrechbare IRPs in die Warteschlange stellt, muss eine Cancel-Routine implementieren. Da IRPs asynchron abgebrochen werden, müssen Sie sicherstellen, dass Ihr Fahrer die daraus resultierenden Rennbedingungen vermeidet. Weitere Informationen zu Racebedingungen im Zusammenhang mit dem Abbrechen von IRPs und deren Vermeidungstechniken finden Sie unter Synchronisieren von IRP-Abbruch .
Jeder vom Treiber erstellte Thread wird mit IRQL = PASSIVE_LEVEL und mit einer Basislaufzeitpriorität ausgeführt, die zuvor festgelegt wurde, als der Treiber PsCreateSystemThread aufgerufen hat. Durch den Aufruf von ExInterlockedRemoveHeadList des Threads wird der IRQL vorübergehend auf DISPATCH_LEVEL des aktuellen Prozessors ausgelöst, während der IRP aus der internen Warteschlange des Treibers entfernt wird. Die ursprüngliche IRQL wird bei der Rückgabe von diesem Aufruf in PASSIVE_LEVEL wiederhergestellt.
Jeder Treiberthread (oder vom Treiber bereitgestellter Workerthreadrückruf) muss die IRQLs, unter denen er ausgeführt wird, sorgfältig verwalten. Berücksichtigen Sie beispielsweise Folgendes:
Da Systemthreads in der Regel mit IRQL = PASSIVE_LEVEL ausgeführt werden, kann ein Treiberthread warten, bis kerneldefinierte Dispatcherobjekte auf den signalierten Zustand festgelegt werden.
Ein gerätededizierter Thread kann z. B. warten, bis andere Treiber ein Ereignis erfüllen und eine bestimmte Anzahl von TEILübertragungs-IRPs abschließen, die der Thread mit IoBuildSynchronousFsdRequest eingerichtet hat.
Ein solcher gerätededienter Thread muss jedoch IRQL auf dem aktuellen Prozessor auslösen, bevor er bestimmte Supportroutinen aufruft.
Wenn ein Treiber beispielsweise DMA verwendet, muss sein gerätededienter Thread seine Aufrufe an AllocateAdapterChannel und FreeAdapterChannel zwischen Aufrufen von KeRaiseIrql und KeLowerIrql schachteln, da diese Routinen und bestimmte andere Supportroutinen für DMA-Vorgänge unter IRQL = DISPATCH_LEVEL aufgerufen werden müssen.
Denken Sie daran, dass StartIo-Routinen auf DISPATCH_LEVEL ausgeführt werden, sodass Treiber, die DMA verwenden, keine Aufrufe der KeXxxIrql-Routinen aus ihren StartIo-Routinen ausführen müssen.
Ein vom Treiber erstellter Thread kann auf auslagerungsfähigen Arbeitsspeicher zugreifen, da er in einem nichtarbiträren Threadkontext (eigener) unter IRQL = PASSIVE_LEVEL ausgeführt wird, aber viele andere Standardtreiberroutinen werden mit IRQL >= DISPATCH_LEVEL ausgeführt. Wenn ein vom Treiber erstellter Thread Arbeitsspeicher zuweist, auf den eine solche Routine zugreifen kann, muss er den Arbeitsspeicher aus einem nicht auslagerten Pool zuordnen. Wenn beispielsweise ein gerätededizierter Thread einen Puffer zuordnet, auf den später der ISR oder SynchCritSection, AdapterControl, AdapterListControl, ControllerControl, DpcForIsr, CustomDpc, IoTimer, CustomTimerDpc oder in einem höheren Treiber ioCompletion-Routine zugreift, kann der threadseitig zugewiesene Arbeitsspeicher nicht ausgelagert werden.
Wenn der Treiber Informationen zu freigegebenem Zustand oder Ressourcen in einer Geräteerweiterung verwaltet, muss ein Treiberthread (z. B . eine StartIo-Routine ) seinen Zugriff auf ein physisches Gerät und die freigegebenen Daten mit den anderen Routinen des Treibers synchronisieren, die auf dasselbe Gerät, denselben Speicherort oder auf dieselben Ressourcen zugreifen.
Wenn der Thread das Gerät oder den Zustand mit dem ISR teilt, muss er KeSynchronizeExecution verwenden, um eine vom Treiber bereitgestellte SynchCritSection-Routine aufzurufen, um das Gerät zu programmieren oder auf den freigegebenen Zustand zuzugreifen. Weitere Informationen finden Sie unter Verwenden kritischer Abschnitte.
Wenn der Thread den Zustand oder die Ressourcen mit anderen Routinen als dem ISR teilt, muss der Treiber den freigegebenen Zustand oder die Ressourcen mit einer vom Treiber initialisierten Executive-Spinsperre schützen, für die der Treiber den Speicher bereitstellt. Weitere Informationen finden Sie unter Spin Locks.
Weitere Informationen zu den Entwurfskonflikten einer, die einen Treiberthread für ein langsames Gerät verwenden, finden Sie unter Abfragen eines Geräts. Siehe auch Verwalten von Hardwareprioritäten. Spezifische Informationen zu IRQLs für bestimmte Supportroutinen finden Sie auf der Referenzseite der Routine.