Freigeben über


Geräteerweiterungen

Bei den meisten Treibern auf mittlerer und niedrigster Ebene ist die Geräteerweiterung die wichtigste Datenstruktur, die einem Geräteobjekt zugeordnet ist. Die interne Struktur ist treiberdefiniert und wird in der Regel für Folgendes verwendet:

  • Verwalten von Gerätestatusinformationen.

  • Stellen Sie Speicher für alle kerneldefinierte Objekte oder andere Systemressourcen bereit, z. B. Drehsperren, die vom Treiber verwendet werden.

  • Halten Sie alle Daten, die der Treiber im Speicher und im Systembereich haben muss, um seine E/A-Operationen auszuführen.

Da die meisten Bus-, Funktions- und Filtertreiber (unterste und zwischengeschaltete Treiber) in einem beliebigen Threadkontext ausgeführt werden (der von jedem Thread, der aktuell ist), ist eine Geräteerweiterung der primäre Ort jedes Treibers, um den Gerätezustand und alle anderen gerätespezifischen Daten zu verwalten, die der Treiber benötigt. Beispielsweise stellt jeder Treiber, der eine CustomTimerDpc - oder CustomDpc-Routine implementiert, normalerweise Speicher für die erforderlichen kerneldefinierten Timer- und/oder DPC-Objekte in einer Geräteerweiterung bereit.

Jeder Treiber, der über einen ISR verfügt, muss Speicher für einen Zeiger auf einen Satz kerneldefinierter Interruptobjekte bereitstellen, und die meisten Gerätetreiber speichern diesen Zeiger in einer Geräteerweiterung. Jeder Treiber bestimmt die Größe der Geräteerweiterung, wenn ein Geräteobjekt erstellt wird, und jeder Treiber definiert den Inhalt und die Struktur seiner eigenen Geräteerweiterungen.

Die I/O-Manager-Routinen "IoCreateDevice" und "IoCreateDeviceSecure " weisen Speicher für das Geräteobjekt und die Erweiterung aus dem nichtpageten Speicherpool zu.

Jede Standardtreiberroutine, die ein IRP empfängt, empfängt auch einen Zeiger auf ein Geräteobjekt, das das Zielgerät für den angeforderten E/A-Vorgang darstellt. Diese Treiberroutinen können über diesen Zeiger auf die entsprechende Geräteerweiterung zugreifen. In der Regel ist ein DeviceObject-Zeiger auch ein Eingabeparameter für den ISR eines Treibers auf der niedrigsten Ebene.

Die folgende Abbildung zeigt einen repräsentativen Satz treiberdefinierter Daten für die Geräteerweiterung eines Geräteobjekts auf der niedrigsten Ebene. Ein Treiber auf höherer Ebene würde keinen Speicher für einen Interruptobjektzeiger bereitstellen, der von IoConnectInterrupt zurückgegeben und an KeSynchronizeExecution und IoDisconnectInterrupt übergeben wird. Ein Treiber auf höherer Ebene würde jedoch Speicher für die Timer- und DPC-Objekte bereitstellen, die in der folgenden Abbildung dargestellt sind, wenn der Treiber über eine CustomTimerDpc-Routine verfügt. Ein Treiber auf höherer Ebene kann auch Speicher für eine Drehsperre für Führungskräfte und eine verriegelte Arbeitswarteschlange bereitstellen.

Diagramm, das eine Beispielgeräteerweiterung für einen Treiber auf der untersten Ebene veranschaulicht.

Zusätzlich zur Bereitstellung von Speicher für einen Interrupt-Objektzeiger muss ein Gerätetreiber auf niedrigster Ebene Speicher für eine Interrupt-Drehsperre bereitstellen, wenn der ISR Interrupts für zwei oder mehr Geräte auf unterschiedlichen Vektoren verarbeitet oder mehr als eine ISR besitzt. Weitere Informationen zum Registrieren eines ISR finden Sie unter Registrieren eines ISR.

In der Regel speichern Gerätetreiber Zeiger auf ihre Geräteobjekte in ihren Geräteerweiterungen, wie es in der Abbildung gezeigt wird. Ein Treiber kann auch eine Kopie der Ressourcenliste für das Gerät in der Erweiterung beibehalten.

Ein höherstufiger Treiber speichert typischerweise einen Zeiger auf das Geräteobjekt des nächstniedrigeren Treibers in seiner Geräteerweiterung. Ein Treiber auf einer höheren Ebene muss einen Zeiger an das Geräteobjekt des nächstniedrigeren Treibers an IoCallDriver übergeben, nachdem er den I/O-Stapelort des nächstniedrigeren Treibers in einem IRP eingerichtet hat, wie in der Verarbeitung von IRPs erläutert.

Beachten Sie auch, dass jeder Treiber auf höherer Ebene, der IRPs für Treiber auf niedrigerer Ebene zuordnet, angeben muss, wie viele Stapelspeicherorte die neuen IRPs aufweisen sollen. Wenn ein Treiber auf höherer Ebene IoMakeAssociatedIrp, IoAllocateIrp oder IoInitializeIrp aufruft, muss er auf das Zielgerätobjekt des nächsten Treibers auf niedrigerer Ebene zugreifen, um seinen StackSize-Wert zu lesen, um die richtige StackSize als Argument für diese Supportroutinen anzugeben.

Während ein Treiber auf höherer Ebene Daten aus dem Geräteobjekt des nächsten niedrigeren Treibers über den von IoAttachDeviceToDeviceStack zurückgegebenen Zeiger lesen kann, muss ein solcher Treiber die folgenden Implementierungsrichtlinien befolgen:

  • Versuchen Sie niemals, Daten in das Geräteobjekt des niedrigeren Treibers zu schreiben.

    Die einzigen Ausnahmen dieser Richtlinie sind Dateisysteme, die DO_VERIFY_VOLUME in den Flags der Geräteobjekte von Wechselmedientreibern auf niedrigerer Ebene festlegen und löschen.

  • Versuchen Sie niemals, aus folgenden Gründen auf die Geräteerweiterung des untergeordneten Treibers zuzugreifen:

    • Es gibt keine sichere Möglichkeit, den Zugriff auf eine einzelne Geräteerweiterung zwischen zwei Treibern zu synchronisieren.

    • Ein Paar Von Treibern, die ein solches Backdoor-Kommunikationsschema implementieren, können nicht einzeln aktualisiert werden, kann keinen Zwischentreiber zwischen ihnen eingefügt haben, ohne vorhandene Treiberquelle zu ändern, und kann nicht neu kompiliert und leicht von einer Windows-Plattform zur nächsten verschoben werden.

Um ihre Interoperabilität mit Treibern auf niedrigerer Ebene von einer Windows-Plattform oder -Version zur nächsten zu erhalten, müssen Treiber auf höherer Ebene entweder die zugehörigen IRPs wiederverwenden oder neue IRPs erstellen, und sie müssen IoCallDriver verwenden, um Anforderungen an Treiber auf niedrigerer Ebene zu kommunizieren.