Share via


Verarbeiten von IRPs in einem Lowest-Level-Treiber

Physische Treiber der niedrigsten Ebene verfügen über bestimmte Standardroutinen, die von Treibern höherer Ebene nicht benötigt werden. Der Satz von Standardroutinen für Treiber der niedrigsten Ebene variiert ebenfalls gemäß den folgenden Kriterien:

  • Die Art des Geräts, das jeder Treiber steuert

  • Gibt an, ob der Treiber seine Geräteobjekte für direkte oder gepufferte E/A-Vorgänge einrichtet.

  • Das Design des einzelnen Fahrers

Um die Rollen der Standardtreiberroutinen zu veranschaulichen, zeigt die folgende Abbildung den Pfad, den ein Beispiel-IRP bei der Verarbeitung durch einen Massenspeichertreiber der niedrigsten Ebene einnimmt. Der Treiber in der Abbildung weist die folgenden Merkmale auf:

  • Das Gerät generiert Am Ende jedes E/A-Vorgangs Interrupts, sodass dieser Treiber über ISR- und DpcForIsr-Routinen verfügt.

  • Der Treiber verfügt über eine StartIo-Routine , anstatt interne Warteschlangen für IRPs einzurichten und seine eigene Warteschlangen zu verwalten.

  • Der Treiber verwendet System-DMA, sodass er die Flags seiner Geräteobjekte für direkte E/A-Vorgänge festlegt und über eine AdapterControl-Routine verfügt .

Diagramm, das einen IRP-Pfad durch Treiberroutinen der niedrigsten Ebene veranschaulicht.

Wie in dieser Abbildung gezeigt, erstellt der E/A-Manager ein IRP und sendet es an die Dispatchroutine des Treibers für den angegebenen Hauptfunktionscode. Wenn der Funktionscode entweder IRP_MJ_READ oder IRP_MJ_WRITE ist, lautet die Dispatchroutine DDDispatchReadWrite.

Aufrufen von IoGetCurrentIrpStackLocation

Jede Treiberroutine, die IRP-Parameter erfordert, muss IoGetCurrentIrpStackLocation aufrufen, um den E/A-Stapelspeicherort des Treibers abzurufen. Solche Routinen umfassen Dispatchroutinen, die mehr als einen wichtigen E/A-Funktionscode (IRP_MJ_*XXX) verarbeiten, eine Funktion verarbeiten, die Nebenfunktionen (IRP_MN_XXX) unterstützt, oder Geräte-E/A-Steuerungsanforderungen (*IRP_MJ_DEVICE_CONTROL und/oder IRP_MJ_INTERNAL_DEVICE_CONTROL) zusammen mit jeder anderen Treiberroutine behandeln, die ein IRP verarbeitet.

Der E/A-Stapelspeicherort dieses Treibers ist der niedrigste, wobei eine unbegrenzte Anzahl von E/A-Stapelspeicherorten auf höherer Ebene schattiert angezeigt wird. Der Einfachheit halber sind Aufrufe von IoGetCurrentIrpStackLocation aus den Routinen DispatchReadWrite, StartIo, AdapterControl und DpcForIsr in der vorherigen Abbildung nicht dargestellt.

Aufrufen von IoMarkIrpPending und IoStartPacket

Der Beispieltreiber führt das IRP nicht in seiner Dispatchroutine durch, sondern verarbeitet den IRP in seiner StartIo-Routine . Bevor dies möglich ist, ruft die Dispatchroutine IoMarkIrpPending auf, um anzugeben, dass die IRP noch nicht abgeschlossen ist. Anschließend wird IoStartPacket aufgerufen, um das IRP für die weitere Verarbeitung durch die StartIo-Routine des Treibers in die Warteschlange zu stellen. Die Dispatchroutine gibt auch den NTSTATUS-Wert STATUS_PENDING zurück.

Die folgende Abbildung veranschaulicht den Aufruf von IoStartPacket.

Diagramm, das einen Aufruf von iostartpacket veranschaulicht.

Wenn der Treiber mit der Verarbeitung eines anderen IRP auf dem Gerät beschäftigt ist, fügt IoStartPacket den IRP in die Gerätewarteschlange ein, die dem Geräteobjekt zugeordnet ist. Der Treiber kann optional einen Key-Wert als Parameter für IoStartPacket bereitstellen, um eine treiberbestimmte Reihenfolge für IRPs in der Gerätewarteschlange festzulegen.

Wenn der Treiber nicht ausgelastet ist und die Gerätewarteschlange leer ist, ruft der E/A-Manager sofort seine StartIo-Routine auf und übergibt die Eingabe-IRP.

Bei Massenspeichergeräten muss der Treiber der niedrigsten Ebene aus zwei Gründen keine Cancel-Routine bereitstellen, wenn ioStartPacket aufgerufen wird:

  1. Ein Dateisystem, das über einen solchen Treiber verfügt, verarbeitet in der Regel den Abbruch von Datei-E/A-Anforderungen.

  2. Treiber für Massenspeichergeräte verarbeiten IRPs schnell.

In der Regel verarbeitet der Treiber der obersten Ebene in einer Kette von mehrstufigen Treibern den Abbruch von IRPs.

Aufrufen von AllocateAdapterChannel und MapTransfer

Wenn die StartIo-Routine in der Abbildung zur Veranschaulichung eines IRP-Pfads durch Treiberroutinen der untersten Ebene dargestellt wird, bestimmt, dass die Übertragungsanforderung durch einen einzelnen DMA-Vorgang ausgeführt werden kann, ruft die StartIo-RoutineAllocateAdapterChannel mit dem Einstiegspunkt der AdapterControl-Routine des Treibers und dem IRP auf.

Wenn der DMA-Controller des Systems verfügbar ist, ruft der E/A-Manager die AdapterControl-Routine des Treibers auf, um den Übertragungsvorgang einzurichten. Die AdapterControl-Routine ruft MapTransfer auf, um den DMA-Controller des Systems einzurichten. Anschließend programmiert der Treiber sein Gerät für den DMA-Vorgang und gibt zurück. (Weitere Informationen zur Verwendung von DMA- und Adapterobjekten finden Sie unter Eingabe-/Ausgabetechniken.)

Aufrufen von IoRequestDpc über die ISR des Treibers

Wenn das Gerät unterbrochen wird, um anzugeben, dass der Übertragungsvorgang abgeschlossen ist, verhindert die ISR des Treibers das Generieren von Interrupts und ruft IoRequestDpc auf, wie in der Abbildung dargestellt, die einen IRP-Pfad durch Treiberroutinen der niedrigsten Ebene veranschaulicht.

Dieser Aufruf stellt die DpcForIsr-Routine des Treibers in die Warteschlange, um einen möglichst großen Teil des Übertragungsvorgangs mit einer niedrigeren Hardwarepriorität (IRQL) abzuschließen.

Aufrufen von IoStartNextPacket und IoCompleteRequest

Wenn die DpcForIsr-Routine die Verarbeitung der Übertragung abgeschlossen hat, ruft sie IoStartNextPacket prompt auf, sodass die StartIo-Routine des Treibers mit dem nächsten IRP in der Gerätewarteschlange aufgerufen wird, sofern eine Warteschlange vorhanden ist. Die DpcForIsr-Routine legt auch den I/O-status block des gerade abgeschlossenen IRP fest und ruft dann IoCompleteRequest für das IRP auf.

Die folgende Abbildung veranschaulicht die Aufrufe dieses Treibers an IoStartNextPacket und IoCompleteRequest.

aufrufen von iostartnextpacket und iocompleterequest.

Treiber sollten IoStartNextPacket oder IoStartNextPacketByKey aufrufen, um den nächsten angeforderten E/A-Vorgang so schnell wie möglich zu starten, vorzugsweise vor dem Aufrufen von IoCompleteRequest.

Wenn IRPs für das Gerät in die Warteschlange eingereiht werden, ruft IoStartNextPacketKeRemoveDeviceQueue auf, um den nächsten IRP aus der Warteschlange zu entfernen. Der E/A-Manager ruft dann die StartIo-Routine des Treibers auf und übergibt den dequeuierten IRP. Wenn sich derzeit keine IRPs in der Gerätewarteschlange befinden, wird IoStartNextPacket lediglich an den Aufrufer zurückgegeben.

Festlegen des E/A-Statusblocks in einem IRP

Jeder Treiber der niedrigsten Ebene muss den I/O-status-Block des IRP festlegen, bevor IoCompleteRequest aufgerufen wird. (In der vorherigen Abbildung kennzeichnet der zweite schattierte Bereich den status-Block.) Der E/A-status-Block liefert Informationen an übergeordnete Treiber und letztendlich an den ursprünglichen Anforderer des E/A-Vorgangs. Jeder Treiber auf höherer Ebene, der über dem Treiber in der vorherigen Abbildung liegt, hat möglicherweise eine IoCompletion-Routine eingerichtet, die den von diesem Treiber festgelegten E/A-status-Block liest. Treiber auf höherer Ebene ändern in der Regel nicht den E/A-status-Block in einem IRP, der von einem Gerätetreiber abgeschlossen wurde, es sei denn, der Treiber auf höherer Ebene versucht den IRP erneut. In diesem Fall wird der E/A-status-Block neu initialisiert.

Jeder Treiber auf höherer Ebene, der einen IRP abschließt, ohne ihn an den nächstniedrigen Treiber zu senden, muss auch den E/A-status-Block in diesem IRP festlegen, bevor IoCompleteRequest aufgerufen wird. Um einen guten E/A-Gesamtdurchsatz zu erzielen, sollte ein Treiber auf höherer Ebene die Parameter an seinem eigenen E/A-Stapelspeicherort jedes IRP überprüfen und, wenn die Parameter ungültig sind, den E/A-status-Block festlegen und die Anforderung selbst abschließen. Wenn möglich, sollte ein Treiber vermeiden, eine ungültige Anforderung an niedrigere Treiber in der Kette weiterzureichen.

Wenn der Übertragungsvorgang in der vorherigen Abbildung erfolgreich ist, legt die DpcForIsr-Routine, die in der Abbildung zur Veranschaulichung eines IRP-Pfads durch Treiberroutinen der untersten Ebene dargestellt ist, STATUS_SUCCESS in Status und die Anzahl der übertragenen Bytes in Information für den I/O-status-Block des IRP fest.

Viele der Standardtreiberroutinen geben auch NTSTATUS-Typwerte zurück. Weitere Informationen zu NTSTATUS-Konstanten wie STATUS_SUCCESS finden Sie unter Protokollierungsfehler.