Freigeben über


Implementieren einer IoCompletion-Routine

Bei der Eingabe empfängt eine IoCompletion-Routine einen Kontextzeiger . Wenn eine Dispatch-Routine IoSetCompletionRoutine aufruft, kann sie einen Kontextzeiger bereitstellen. Dieser Zeiger kann auf die treiberbestimmten Kontextinformationen verweisen, die die IoCompletion-Routine zum Verarbeiten eines IRP benötigt. Der Kontextbereich kann nicht seitenfähig sein, da die IoCompletion-Routine unter IRQL = DISPATCH_LEVEL aufgerufen werden kann.

Beachten Sie die folgenden Implementierungsrichtlinien für IoCompletion-Routinen:

  • Eine IoCompletion-Routine kann den I/O-Statusblock des IRP überprüfen, um das Ergebnis des E/A-Vorgangs zu ermitteln.

  • Wenn die Dispatch-Routine die Eingabe-IRP mithilfe von IoAllocateIrp oder IoBuildAsynchronousFsdRequest zugewiesen hat, muss die IoCompletion-RoutineIoFreeIrp aufrufen, um dieses IRP freizugeben, vorzugsweise bevor es das ursprüngliche IRP abgeschlossen.

    • Die IoCompletion-Routine muss alle IRP-bezogene Ressourcen freigeben, die die Verteilerroutine für das vom Treiber zugewiesene IRP zugeordnet hat, vorzugsweise bevor die Verteilerroutine das entsprechende IRP freigibt.

      Wenn die Dispatch-Routine eine MDL mit IoAllocateMdl zuweist und IoBuildPartialMdl für einen Teilübertragungs-IRP aufruft, muss die IoCompletion-Routine die MDL mit IoFreeMdl freigeben. Wenn es Ressourcen zuweist, um den Zustand des ursprünglichen IRP aufrechtzuerhalten, muss es diese Ressourcen freigeben, vorzugsweise bevor es IoCompleteRequest mit dem ursprünglichen IRP aufruft, und auf jeden Fall bevor es die Kontrolle zurückgibt.

      Im Allgemeinen sollte die IoCompletion-Routine vor dem Freigeben oder Abschließen einer IRP-Routine alle pro IRP-Ressourcen freigeben, die von der Versandroutine zugeordnet werden. Andernfalls muss der Treiber den Zustand über die Ressourcen, die freigegeben werden sollen, aufrechterhalten, bevor die IoCompletion-Routine die Kontrolle nach dem Abschluss der ursprünglichen Anforderung zurückgibt.

    • Wenn die IoCompletion-Routine das ursprüngliche IRP mit STATUS_SUCCESS nicht abschließen kann, muss der E/A-Statusblock im ursprünglichen IRP auf den Wert festgelegt werden, der im vom Treiber zugewiesenen IRP zurückgegeben wird, was dazu führte, dass die IoCompletion-Routine die ursprüngliche Anforderung fehlschlug.

    • Wenn die IoCompletion-Routine die ursprüngliche Anforderung mit STATUS_PENDING abgeschlossen hat, muss ioMarkIrpPending mit dem ursprünglichen IRP aufgerufen werden, bevor IoCompleteRequest aufgerufen wird.

    • Wenn die IoCompletion-Routine das ursprüngliche IRP mit einem Fehler STATUS_XXX fehlschlagen muss, kann ein Fehler protokolliert werden. Es liegt jedoch in der Verantwortung des zugrunde liegenden Gerätetreibers, alle aufgetretenen Geräte-E/A-Fehler zu protokollieren, sodass IoCompletion-Routinen in der Regel keine Fehler protokollieren.

    • Wenn die IoCompletion-Routine das vom Treiber zugeordnete IRP verarbeitet und freigibt, muss sie die Steuerung mit STATUS_MORE_PROCESSING_REQUIRED zurückgeben.

      Gibt STATUS_MORE_PROCESSING_REQUIRED in der IoCompletion-Routine zurück, verhindert die Abschlussverarbeitung des I/O-Managers für einen vom Treiber zugewiesenen und freigegebenen IRP. Ein zweiter Aufruf von IoCompleteRequest bewirkt, dass der I/O-Manager das Aufrufen der IRP-Abschlussroutinen fortsetzt, beginnend mit der Abschlussroutine unmittelbar über der Routine, die STATUS_MORE_PROCESSING_REQUIRED zurückgegeben hat.

  • Wenn die IoCompletion-Routine einen eingehenden IRP wiederverwendet, um eine oder mehrere Anforderungen an niedrigere Treiber zu senden, oder wenn die Routine fehlgeschlagene Vorgänge wiederholt, sollte sie den Kontext aktualisieren, den die IoCompletion-Routine über jede Wiederverwendung oder Wiederholung des IRP verwaltet. Anschließend kann er erneut die E/A-Stackposition des nächsttieferen Treibers einrichten, IoSetCompletionRoutine mit seinem eigenen Einstiegspunkt aufrufen und IoCallDriver für den IRP (I/O Request Packet) aufrufen.

    • Die IoCompletion-Routine sollte bei jeder Wiederverwendung oder Wiederholung des IRP nicht IoMarkIrpPending aufrufen.

      Die Versandroutine markierte bereits das ursprüngliche IRP als ausstehend. Bis alle Treiber in der Kette das ursprüngliche IRP mit IoCompleteRequest abgeschlossen haben, bleibt das IRP ausstehend.

    • Vor dem Wiederholen einer Anforderung sollte die IoCompletion-Routine den E/A-Statusblock mit STATUS_SUCCESS für Status und Null für Informationen zurücksetzen, möglicherweise nach dem Speichern der zurückgegebenen Fehlerinformationen.

      Bei jedem Wiederholungsversuche dekrementiert die IoCompletion-Routine in der Regel eine wiederholungsanzahl, die von der Dispatch-Routine eingerichtet wurde. In der Regel muss die IoCompletion-RoutineIoCompleteRequest aufrufen, um das IRP fehlzuschlagen, wenn eine begrenzte Anzahl von Wiederholungen fehlgeschlagen ist.

    • Die IoCompletion-Routine muss STATUS_MORE_PROCESSING_REQUIRED zurückgeben, nachdem sie IoSetCompletionRoutine und IoCallDriver mit einem IRP aufgerufen hat, das erneut verwendet oder wiederholt wird.

      Gibt STATUS_MORE_PROCESSING_REQUIRED aus der IoCompletion-Routine zurück, um die Abschlussverarbeitung eines wiederverwendeten oder erneut versuchten IRP durch den I/O-Manager zu verzögern.

    • Wenn die IoCompletion-Routine das ursprüngliche IRP mit STATUS_SUCCESS nicht abschließen kann, muss sie den E/A-Statusblock unverändert lassen, wie er von niedrigeren Treibern für den Wiederverwendungs- oder Wiederholungsvorgang zurückgegeben wurde, was dazu führt, dass die IoCompletion-Routine das IRP fehlschlagen lässt.

    • Wenn die IoCompletion-Routine die ursprüngliche Anforderung mit STATUS_PENDING abschließt, muss ioMarkIrpPending mit dem ursprünglichen IRP aufgerufen werden, bevor IoCompleteRequest aufgerufen wird.

    • Wenn die IoCompletion-Routine das ursprüngliche IRP mit einem Fehler STATUS_XXX fehlschlagen muss, kann ein Fehler protokolliert werden. Es liegt jedoch in der Verantwortung des zugrunde liegenden Gerätetreibers, alle aufgetretenen Geräte-E/A-Fehler zu protokollieren, sodass IoCompletion-Routinen in der Regel keine Fehler protokollieren.

  • Jeder Treiber, der eine IoCompletion-Routine in einem IRP festlegt und dann das IRP an einen niedrigeren Treiber übergibt, sollte das IRP-PendingReturned-Flag> in der IoCompletion-Routine überprüfen. Wenn das Flag festgelegt ist, muss die IoCompletion-RoutineIoMarkIrpPending mit dem IRP aufrufen. Ein Treiber, der das IRP übergibt und dann auf ein Ereignis wartet, sollte das IRP nicht als ausstehend kennzeichnen. Stattdessen sollte die IoCompletion-Routine das Ereignis signalisieren und STATUS_MORE_PROCESSING_REQUIRED zurückgeben.

  • Die IoCompletion-Routine muss alle Ressourcen freigeben, die die Verteilerroutine zur Verarbeitung des ursprünglichen IRP zugeordnet hat, vorzugsweise bevor die IoCompletion-RoutineIoCompleteRequest mit dem ursprünglichen IRP aufruft, und definitiv bevor die IoCompletion-Routine die Kontrolle nach Abschluss des ursprünglichen IRP zurückgibt.

Wenn ein höherer Treiber seine IoCompletion-Routine in der ursprünglichen IRP festgelegt hat, wird die IoCompletion-Routine dieses Treibers erst aufgerufen, wenn die IoCompletion-Routinen aller Treiber auf niedrigerer Ebene aufgerufen wurden.

Bereitstellen einer Prioritätsverstärkung bei Aufrufen von IoCompleteRequest

Wenn ein Gerätetreiber auf niedrigster Ebene ein IRP in seiner Dispatch-Routine abschließen kann, ruft er IoCompleteRequest mit einem PriorityBoost von IO_NO_INCREMENT auf. Es ist keine Erhöhung der Laufzeitpriorität erforderlich, da der Treiber davon ausgehen kann, dass der ursprüngliche Antragsteller nicht auf den Abschluss des E/A-Vorgangs gewartet hat.

Andernfalls stellt der Treiber der niedrigsten Ebene einen systemdefinierten und gerätetypspezifischen Wert bereit, der die Laufzeitpriorität des Antragstellers erhöht, um die Zeit zu kompensieren, die der Antragsteller auf seine Geräte-E/A-Anforderung wartete. Die Verstärkungswerte finden Sie unter Wdm.h oder Ntddk.h.

Treiber höherer Ebene wenden den gleichen PriorityBoost wie die jeweiligen zugrunde liegenden Gerätetreiber an, wenn sie IoCompleteRequest aufrufen.

Auswirkung des Aufrufens von IoCompleteRequest

Wenn ein Treiber IoCompleteRequest aufruft, füllt der E/A-Manager den I/O-Stapelspeicherort dieses Treibers mit Nullen aus, bevor er ggf. den nächsten Treiber auf höherer Ebene aufruft, der eine IoCompletion-Routine eingerichtet hat, die für das IRP aufgerufen werden soll.

Die IoCompletion-Routine eines höheren Treibers kann nur den I/O-Statusblock des IRP überprüfen, um zu bestimmen, wie alle niedrigeren Treiber die Anforderung verarbeitet haben.

Der Aufrufer von IoCompleteRequest darf nicht versuchen, auf das soeben abgeschlossene IRP zuzugreifen. Ein solcher Versuch ist ein Programmierfehler, der einen Systemabsturz verursacht.