Verarbeiten von IRPs in einem Intermediate-Level Driver

Treiber auf höherer Ebene verfügen über einen anderen Satz von Standardroutinen als Gerätetreiber der untersten Ebene, mit einer überlappenden Teilmenge von Standardroutinen, die für beide Treibertypen gemeinsam sind.

Der Satz von Routinen für fortgeschrittene und höchste Treiber variiert ebenfalls gemäß den folgenden Kriterien:

  • Die Art des zugrunde liegenden physischen Geräts

  • Gibt an, ob ein zugrunde liegender Gerätetreiber Geräteobjekte für direkte oder gepufferte E/A-Vorgänge einrichtet

  • Das Design des einzelnen übergeordneten Treibers

Die folgende Abbildung veranschaulicht den Pfad, den ein IRP durch die Standardroutinen eines zwischengeschalteten Spiegel Treibers führen kann, der sich über den im vorherigen Abschnitt beschriebenen Gerätetreiber der untersten Ebene befindet.

Der in der folgenden Abbildung gezeigte Treiber weist die folgenden Merkmale auf:

  • Der Treiber wird über mehrere physische Geräte und möglicherweise über mehrere Gerätetreiber verteilt.

  • Der Treiber ordnet manchmal zusätzliche IRPs für Treiber niedrigerer Ebene zu, abhängig vom angeforderten Vorgang im Eingabe-IRP.

  • Der Treiber verfügt über mindestens einen Dateisystemtreiber, der darüber liegt, und dieser Dateisystemtreiber kann über andere Zwischentreiber auf einer höheren Ebene als dieser liegen.

Diagramm, das einen IRP-Pfad durch zwischengeschaltete Treiberroutinen veranschaulicht.

Wie in der Abbildung gezeigt, erstellt der E/A-Manager ein IRP und sendet es für den angegebenen Hauptfunktionscode an die Dispatchroutine des Treibers. Wenn der Funktionscode IRP_MJ_WRITE ist, lautet die Dispatchroutine DDDispatchWrite. Die E/A-Stapelposition des zwischengeschalteten Treibers wird in der Mitte angezeigt, wobei eine unbegrenzte Anzahl von E/A-Stapelspeicherorten für Treiber auf höherer und niedrigerer Ebene schattiert angezeigt wird.

Zuweisung von IRPs

Der zweck des Spiegel Treibers besteht darin, Schreibanforderungen an mehrere physische Geräte zu senden und alternativ Leseanforderungen an die Treiber dieser Geräte zu senden. Für Schreibanforderungen erstellt der Treiber doppelte IRPs für jedes Gerät, auf dem die Daten geschrieben werden sollen, vorausgesetzt, die Parameter im Eingabe-IRP sind gültig.

Die vorherige Abbildung zeigt einen Aufruf von IoAllocateIrp , aber Treiber auf höherer Ebene können andere Supportroutinen aufrufen, um IRPs für Treiber niedrigerer Ebene zuzuweisen. Weitere Informationen finden Sie unter Erstellen von IRPs für Lower-Level Treiber.

Wenn die Dispatchroutine IoAllocateIrp aufruft, gibt sie die Anzahl der E/A-Stapelspeicherorte an, die für das IRP erforderlich sind. Der Treiber muss einen Stapelspeicherort für jeden unteren Treiber in der Kette angeben und den entsprechenden Wert aus den Geräteobjekten jedes Treibers direkt unterhalb des Spiegel Treibers abrufen. Optional kann der Treiber diesen Wert hinzufügen, wenn er IoAllocateIrp aufruft , um einen eigenen Stapelspeicherort für jedes zuzuordnende IRP abzurufen, wie es der Treiber in der vorherigen Abbildung tut.

Die Dispatchroutine dieses Zwischentreibers ruft IoGetCurrentIrpStackLocation (nicht gezeigt) mit dem ursprünglichen IRP auf, um Parameter zu überprüfen.

Sie ruft IoSetNextIrpStackLocation auf, da sie in jedem neu erstellten IRP und IoGetCurrentIrpStackLocation einen eigenen Stapelspeicherort zugeordnet hat, um einen Kontext für sich selbst zu erstellen, den sie später in der IoCompletion-Routine verwendet.

Als Nächstes wird IoGetNextIrpStackLocation mit jedem neu erstellten IRP aufgerufen, sodass die E/A-Stapelspeicherorte der nächsten niedrigeren Treiberebene in den zugeordneten IRPs eingerichtet werden können. Die Spiegel Dispatchroutine des Treibers kopiert die IRP-Funktionscodes und -parameter (Zeiger auf den Übertragungspuffer, Länge in Bytes, die für IRP_MJ_WRITE übertragen werden soll) in die E/A-Stapelspeicherorte für die nächstniedrigen Treiber. Diese Treiber wiederum richten die E/A-Stapelspeicherorte für die Treiber direkt darunter ein, falls vorhanden.

Aufrufen von IoSetCompletionRoutine und IoCallDriver

Die Dispatchroutine in der vorherigen Abbildung ruft IoSetCompletionRoutine für jedes zugeordnete IRP auf. Da der Treiber in der vorherigen Abbildung die zugeordneten IRPs löschen muss, legt dieser Treiber seine IoCompletion-Routine so fest, dass aufgerufen wird, wenn niedrigere Treiber seine IRPs abschließen, unabhängig davon, ob der E/A-Vorgang erfolgreich abgeschlossen, fehlgeschlagen oder abgebrochen wurde.

Da der Treiber in der vorherigen Abbildung parallel spiegelt, übergibt er beide IRPs, die er zugeordnet hat, an die Treiber der nächstniedrigen Ebene, indem IoCallDriver zweimal aufgerufen wird, einmal für jedes Zielgeräteobjekt, das eine gespiegelte Partition darstellt.

Verarbeiten von IRPs in der IoCompletion-Routine des Treibers

Wenn einer der Treiber auf niedrigerer Ebene den angeforderten Vorgang abschließt, ruft der E/A-Manager die IoCompletion-Routine des zwischengeschalteten Spiegel Treibers auf. Der Spiegel Treiber verwaltet eine Anzahl an seinem eigenen E/A-Stapelspeicherort für den ursprünglichen IRP, um nachzuverfolgen, wann die niedrigeren Treiber alle doppelten IRPs abgeschlossen haben.

Unter der Annahme, dass der E/A-status-Block angibt, dass ein Satz niedrigerer Treiber den in der vorherigen Abbildung gezeigten doppelten IRP abgeschlossen hat, verringert die IoCompletion-Routine des Spiegel Treibers ihre Anzahl, kann aber den ursprünglichen IRP erst abschließen, wenn er die Anzahl auf 0 (null) verringert. Wenn die dekrementierte Anzahl noch nicht 0 (null) ist, ruft die IoCompletion-RoutineIoFreeIrp mit dem zuerst zurückgegebenen IRP (DupIRP1 in der vorherigen Abbildung) auf, den der Treiber zugewiesen hat, und gibt STATUS_MORE_PROCESSING_REQUIRED zurück.

Wenn die ioCompletion-Routine des Spiegel Treibers mit dem in der vorherigen Abbildung gezeigten DupIRP2 erneut aufgerufen wird, verringert die IoCompletion-Routine die Anzahl im ursprünglichen IRP und ermittelt, dass beide Gruppen von Treibern niedrigerer Ebene die angeforderten Vorgänge ausgeführt haben.

Wenn der E/A-status-Block in DupIRP2 ebenfalls mit STATUS_SUCCESS festgelegt ist, kopiert die IoCompletion-Routine den E/A-status-Block aus DupIRP2 in das ursprüngliche IRP und gibt DupIRP2 frei. Es ruft IoCompleteRequest mit dem ursprünglichen IRP auf und gibt STATUS_MORE_PROCESSING_REQUIRED zurück. Die Rückgabe dieses status verhindert, dass der E/A-Manager eine weitere Vervollständigungsverarbeitung auf DupIRP2 versucht. Da der IRP keinem Thread zugeordnet ist, sollte seine Abschlussverarbeitung mit dem Treiber enden, der ihn erstellt hat.

Wenn einer der Treiber auf niedrigerer Ebene die IRPs des Spiegel Treibers nicht erfolgreich abgeschlossen hat, sollte die IoCompletion-Routine des Spiegel Treibers einen Fehler protokollieren und versuchen, eine geeignete Gespiegelte Datenwiederherstellung zu versuchen. Weitere Informationen finden Sie unter Protokollierungsfehler.