Freigeben über


Verwalten von Gerätewarteschlangen

Der E/A-Manager erstellt normalerweise (mit Ausnahme von FSDs) ein zugeordnetes Gerätewarteschlangenobjekt, wenn ein Treiber IoCreateDevice aufruft. Außerdem werden IoStartPacket und IoStartNextPacket bereitgestellt, die Treiber aufrufen können, damit der E/A-Manager IRPs in die zugehörige Gerätewarteschlange einfügt oder ihre StartIo-Routinen aufruft.

Daher ist es selten erforderlich (oder besonders nützlich), dass ein Treiber eigene Gerätewarteschlangenobjekte für IRPs einrichten kann. Wahrscheinliche Kandidaten sind Treiber, z. B. der SCSI-Porttreiber, die eingehende IRPs von einer Bestimmten Anzahl eng gekoppelter Klassentreiber für heterogene Geräte koordinieren müssen, die über einen einzelnen Controller oder Busadapter gewartet werden.

Anders ausgedrückt: Ein Treiber für einen Datenträgerarraycontroller verwendet eher ein vom Treiber erstelltes Controllerobjekt als zusätzliche Gerätewarteschlangenobjekte, während ein Treiber für einen Add-On-Busadapter und eine Reihe von Klassentreibern etwas wahrscheinlicher zusätzliche Gerätewarteschlangen verwendet.

Verwenden zusätzlicher Gerätewarteschlangen mit einer StartIo-Routine

Durch Aufrufen von IoStartPacket und IoStartNextPacket synchronisieren die Dispatch- und DpcForIsr-Routinen (oder CustomDpc) eines Treibers Aufrufe mit der StartIo-Routine mithilfe der Gerätewarteschlange, die der E/A-Manager beim Erstellen des Geräteobjekts erstellt hat. Für einen Porttreiber mit einer StartIo-Routine fügen IoStartPacket und IoStartNextPacket IRPs in die Gerätewarteschlange für den freigegebenen Gerätecontroller/Adapter des Porttreibers ein, und entfernen Sie sie. Wenn der Porttreiber auch zusätzliche Gerätewarteschlangen einrichtet, um Anforderungen von eng gekoppelten Treibern höherer Klasse aufzunehmen, muss er eingehende IRPs in seinen zusätzlichen Gerätewarteschlangen "sortieren", normalerweise in seiner StartIo-Routine .

Der Porttreiber muss ermitteln, in welcher zusätzlichen Gerätewarteschlange jedes IRP gehört, bevor er versucht, diese IRP in die entsprechende Warteschlange einzufügen. Ein Zeiger auf das Zielgerätobjekt wird mit dem IRP an die Dispatch-Routine des Treibers übergeben. Der Treiber sollte den Zeiger zur Verwendung beim "Sortieren" der eingehenden IRPs speichern. Beachten Sie, dass der an die StartIo-Routine übergebene Geräteobjektzeiger das eigene Geräteobjekt des Treibers ist, das den Gerätecontroller/-adapter darstellt, sodass er nicht für diesen Zweck verwendet werden kann.

Nach dem Anstehen aller IRPs programmiert der Treiber seinen freigegebenen Controller/Adapter, um die Anforderung auszuführen. Daher kann der Porttreiber eingehende Anforderungen für alle Geräte auf einer First-Come-, First-Served-Basis verarbeiten, bis ein Aufruf von KeInsertDeviceQueue ein IRP in die Gerätewarteschlange eines bestimmten Klassentreibers versetzt.

Durch Die Verwendung einer eigenen Gerätewarteschlange für alle IRPs, die über die StartIo-Routine verarbeitet werden sollen, serialisiert der zugrunde liegende Porttreiber Vorgänge über den controller/adapter des freigegebenen Geräts (oder Des Busses) an alle angeschlossenen Geräte. Da dieser Porttreiber manchmal IRPs für jedes unterstützte Gerät in einer separaten Gerätewarteschlange hält, hemmt dieser Porttreiber die Verarbeitung von IRPs für ein bereits ausgelastetes Gerät, während er den E/A-Durchsatz für jedes andere Gerät erhöht, das E/A über die freigegebene Hardware ausführt.

Als Reaktion auf den Aufruf von IoStartPacket aus der Dispatch-Routine des Porttreibers ruft der E/A-Manager entweder sofort die StartIo-Routine dieses Treibers auf oder platziert die IRP in der Gerätewarteschlange, die dem Geräteobjekt für den freigegebenen Controller/Adapter des Porttreibers zugeordnet ist.

Der Porttreiber muss seine eigenen Zustandsinformationen zu jedem der heterogenen Geräte verwalten, die er über den freigegebenen Gerätecontroller/-adapter bereitstellt.

Beachten Sie Folgendes beim Entwerfen von Klassen-/Porttreibern mit zusätzlichen Gerätewarteschlangen:

  • Ein Treiber kann nicht einfach einen Zeiger auf ein Geräteobjekt abrufen, das von einem Treiber erstellt wird, der über sich selbst liegt, mit Ausnahme des Geräteobjekts am oberen Rand des Gerätestapels.

    Der E/A-Manager bietet standardmäßig keine Unterstützungsroutine für einen solchen Zeiger. Darüber hinaus macht die Reihenfolge, in der Treiber geladen werden, es für niedrigere Treiber unmöglich, Zeiger für Geräteobjekte höherer Treiber zu erhalten, die noch nicht erstellt wurden, wenn ein Treiber auf niedrigerer Ebene sein Gerät hinzufügt.

    Obwohl IoGetAttachedDeviceReference einen Zeiger auf das Geräteobjekt der höchsten Ebene in einem Treiberstapel zurückgibt, sollte ein Treiber diesen Zeiger nur verwenden, um ein Ziel für E/A-Anforderungen an seinen Stapel festzulegen. Ein Treiber sollte nicht versuchen, das Geräteobjekt zu lesen oder zu schreiben.

  • Ein Treiber kann keinen Zeiger auf ein Geräteobjekt verwenden, das von einem Treiber erstellt wurde, der über sich selbst liegt, außer anforderungen an den Anfang des eigenen Gerätestapels zu senden.

    Es gibt keine Möglichkeit, den Zugriff auf ein einzelnes Geräteobjekt (und dessen Geräteerweiterung) zwischen zwei Treibern multiprozessorsicher zu synchronisieren. Keiner der Treiber kann annahmen, welche E/A-Verarbeitung der andere Treiber derzeit ausführt.

Selbst bei eng gekoppelten Klassen-/Porttreibern sollte jeder Klassentreiber den Zeiger auf die Geräteobjekte des Porttreibers nur verwenden, um IRPs mit IoCallDriver zu übergeben. Der zugrunde liegende Porttreiber muss seinen eigenen Zustand beibehalten, wahrscheinlich in der Geräteerweiterung des Porttreibers, über Anforderungen, die er für alle eng gekoppelten Klassentreiber verarbeitet.

Verwalten zusätzlicher Gerätewarteschlangen über Treiberroutinen hinweg

Alle Porttreiber, die IRPs in zusätzlichen Gerätewarteschlangen für einen eng gekoppelten Satz von Klassentreibern in die Warteschlange stellen, müssen auch die folgende Situation effizient behandeln:

  1. Die Dispatch-Routinen haben IRPs für ein bestimmtes Gerät in die vom Treiber erstellte Gerätewarteschlange für dieses Gerät eingefügt.

  2. IRPs für andere Geräte werden weiterhin in die StartIo-Routine des Treibers mit IoStartPacket eingereiht und über den gemeinsam genutzten Gerätecontroller verarbeitet.

  3. Der Gerätecontroller wird nicht im Leerlauf, aber jede IRP in der vom Treiber erstellten Gerätewarteschlange muss so schnell wie möglich in die StartIo-Routine des Treibers eingereiht werden.

Folglich muss die DpcForIsr-Routine des Porttreibers versuchen, eine IRP aus der internen Gerätewarteschlange des Treibers für ein bestimmtes Gerät in die Gerätewarteschlange für den freigegebenen Adapter/Controller zu übertragen, sobald der Porttreiber eine IRP abgeschlossen hat, wie folgt:

  1. Die DpcForIsr-Routine ruft IoStartNextPacket auf, damit die StartIo-Routine mit der Verarbeitung des nächsten IRP in die Warteschlange für den gemeinsam genutzten Gerätecontroller beginnt.

  2. Die DpcForIsr-Routine ruft KeRemoveDeviceQueue auf, um die nächste IRP (falls vorhanden) zu dequeue, die sie in ihrer internen Gerätewarteschlange für das Gerät hält, in dessen Auftrag es eine IRP abschließen wird.

  3. Wenn KeRemoveDeviceQueue einen Nicht-NULL-Zeiger zurückgibt, ruft die DpcForIsr-RoutineIoStartPacket mit dem soeben dequeuierten IRP auf, damit es in die Warteschlange für den controller/adapter des freigegebenen Geräts eingereiht wird. Andernfalls setzt der Aufruf von KeRemoveDeviceQueue einfach den Zustand des Gerätewarteschlangenobjekts auf Not-Busy zurück, und die DpcForIsr-Routine lässt den Aufruf von IoStartPacket aus.

  4. Anschließend ruft die DpcForIsr-RoutineIoCompleteRequest mit der Eingabe-IRP auf, für die der Porttreiber gerade die E/A-Verarbeitung abgeschlossen hat, entweder durch Festlegen des E/A-status-Blocks mit einem Fehler oder durch Erfüllung der E/A-Anforderung.

Beachten Sie, dass die vorherige Sequenz impliziert, dass die DpcForIsr-Routine auch das Gerät bestimmen muss, für das sie die aktuelle (Eingabe)-IRP abschließt, um interne Warteschlangen von IRPs effizient zu verwalten.

Wenn der Porttreiber versucht, zu warten, bis sein freigegebener Controller/Adapter im Leerlauf ist, bevor er irPs in seinen zusätzlichen Gerätewarteschlangen entfernt, kann der Treiber ein Gerät verhungern, für das eine hohe E/A-Nachfrage bestand, während er sofort jedes andere Gerät bedient, für das die aktuelle E/A-Nachfrage tatsächlich viel geringer war.