Minidriver, Miniport-Treiber und Treiberpaare

Ein Minidriver oder ein Miniporttreiber fungiert als hälfte eines Fahrerpaars. Treiberpaare wie (Miniport, Port) können die Treiberentwicklung erleichtern. In einem Treiberpaar verarbeitet ein Treiber allgemeine Aufgaben, die für eine ganze Sammlung von Geräten gemeinsam sind, während der andere Treiber Aufgaben verarbeitet, die für ein einzelnes Gerät spezifisch sind. Die Treiber, die gerätespezifische Aufgaben verarbeiten, werden unter einer Vielzahl von Namen verwendet, einschließlich Miniporttreiber, Miniklassentreiber und Minidriver.

Microsoft stellt den allgemeinen Treiber bereit, und in der Regel stellt ein unabhängiger Hardwareanbieter den spezifischen Treiber bereit. Bevor Sie dieses Thema lesen, sollten Sie sich mit den Ideen vertraut machen, die in Geräteknoten und Gerätestapeln und E/A-Anforderungspaketen vorgestellt werden.

Jeder Kernelmodustreiber muss eine Funktion namens DriverEntry implementieren, die kurz nach dem Laden des Treibers aufgerufen wird. Die DriverEntry-Funktion füllt bestimmte Elemente einer DRIVER_OBJECT-Struktur mit Zeigern auf mehrere andere Funktionen aus, die der Treiber implementiert. Beispielsweise füllt die DriverEntry-Funktion den Unload-Member der DRIVER_OBJECT-Struktur mit einem Zeiger auf die Unload-Funktion des Treibers aus, wie im folgenden Diagramm dargestellt.

Diagramm, das die Treiber-Objekt-Struktur mit dem Entladeelement zeigt.

Das MajorFunction-Element der DRIVER_OBJECT-Struktur ist ein Array von Zeigern auf Funktionen, die E/A-Anforderungspakete (IRPs) verarbeiten, wie im folgenden Diagramm dargestellt. In der Regel füllt der Treiber mehrere Member des MajorFunction-Arrays mit Zeigern auf Funktionen (vom Treiber implementiert), die verschiedene Arten von IRPs verarbeiten.

Diagramm, das die Treiber-Objekt-Struktur mit dem Hauptfunktionselement zeigt.

Ein IRP kann nach seinem Hauptfunktionscode kategorisiert werden, der durch eine Konstante wie IRP_MJ_READ, IRP_MJ_WRITE oder IRP_MJ_PNP identifiziert wird. Die Konstanten, die Hauptfunktionscode identifizieren, dienen als Indizes im MajorFunction-Array . Angenommen, der Treiber implementiert eine Dispatch-Funktion, um IRPs zu verarbeiten, die über den Hauptfunktionscode IRP_MJ_WRITE verfügen. In diesem Fall muss der Treiber das Element MajorFunction[IRP_MJ_WRITE] des Arrays mit einem Zeiger auf die Dispatchfunktion ausfüllen.

In der Regel füllt der Treiber einige Elemente des MajorFunction-Arrays aus und lässt die restlichen Elemente auf Standardwerte festgelegt, die vom E/A-Manager bereitgestellt werden. Das folgende Beispiel zeigt, wie Sie die Debuggererweiterung !drvobj verwenden, um die Funktionszeiger für den Parporttreiber zu überprüfen.

0: kd> !drvobj parport 2
Driver object (fffffa80048d9e70) is for:
 \Driver\Parport
DriverEntry:   fffff880065ea070 parport!GsDriverEntry
DriverStartIo: 00000000 
DriverUnload:  fffff880065e131c parport!PptUnload
AddDevice:     fffff880065d2008 parport!P5AddDevice

Dispatch routines:
[00] IRP_MJ_CREATE                      fffff880065d49d0    parport!PptDispatchCreateOpen
[01] IRP_MJ_CREATE_NAMED_PIPE           fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[02] IRP_MJ_CLOSE                       fffff880065d4a78    parport!PptDispatchClose
[03] IRP_MJ_READ                        fffff880065d4bac    parport!PptDispatchRead
[04] IRP_MJ_WRITE                       fffff880065d4bac    parport!PptDispatchRead
[05] IRP_MJ_QUERY_INFORMATION           fffff880065d4c40    parport!PptDispatchQueryInformation
[06] IRP_MJ_SET_INFORMATION             fffff880065d4ce4    parport!PptDispatchSetInformation
[07] IRP_MJ_QUERY_EA                    fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[08] IRP_MJ_SET_EA                      fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[09] IRP_MJ_FLUSH_BUFFERS               fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0b] IRP_MJ_SET_VOLUME_INFORMATION      fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0c] IRP_MJ_DIRECTORY_CONTROL           fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[0e] IRP_MJ_DEVICE_CONTROL              fffff880065d4be8    parport!PptDispatchDeviceControl
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     fffff880065d4c24    parport!PptDispatchInternalDeviceControl
[10] IRP_MJ_SHUTDOWN                    fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[11] IRP_MJ_LOCK_CONTROL                fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[12] IRP_MJ_CLEANUP                     fffff880065d4af4    parport!PptDispatchCleanup
[13] IRP_MJ_CREATE_MAILSLOT             fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[14] IRP_MJ_QUERY_SECURITY              fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[15] IRP_MJ_SET_SECURITY                fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[16] IRP_MJ_POWER                       fffff880065d491c    parport!PptDispatchPower
[17] IRP_MJ_SYSTEM_CONTROL              fffff880065d4d4c    parport!PptDispatchSystemControl
[18] IRP_MJ_DEVICE_CHANGE               fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[19] IRP_MJ_QUERY_QUOTA                 fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[1a] IRP_MJ_SET_QUOTA                   fffff80001b6ecd4    nt!IopInvalidDeviceRequest
[1b] IRP_MJ_PNP                         fffff880065d4840    parport!PptDispatchPnp

In der Debuggerausgabe sehen Sie, dass parport.sys GsDriverEntry implementiert, den Einstiegspunkt für den Treiber. GsDriverEntry, das beim Erstellen des Treibers automatisch generiert wurde, führt eine Initialisierung durch und ruft dann DriverEntry auf, was vom Treiberentwickler implementiert wurde.

Sie können auch sehen, dass der Parporttreiber (in seiner DriverEntry-Funktion ) Zeiger zum Senden von Funktionen für diese hauptfunktionscodes bereitstellt:

  • IRP_MJ_CREATE
  • IRP_MJ_CLOSE
  • IRP_MJ_READ
  • IRP_MJ_WRITE
  • IRP_MJ_QUERY_INFORMATION
  • IRP_MJ_SET_INFORMATION
  • IRP_MJ_DEVICE_CONTROL
  • IRP_MJ_INTERNAL_DEVICE_CONTROL
  • IRP_MJ_CLEANUP
  • IRP_MJ_POWER
  • IRP_MJ_SYSTEM_CONTROL
  • IRP_MJ_PNP

Die restlichen Elemente des MajorFunction-Arrays enthalten Zeiger auf die Standardverteilungsfunktion nt! IopInvalidDeviceRequest.

In der Debuggerausgabe sehen Sie, dass der Parporttreiber Funktionszeiger für Unload und AddDevice bereitgestellt hat, aber keinen Funktionszeiger für StartIo bereitgestellt hat. Die AddDevice-Funktion ist ungewöhnlich, da ihr Funktionszeiger nicht in der DRIVER_OBJECT-Struktur gespeichert ist. Stattdessen wird es im AddDevice-Member einer Erweiterung der DRIVER_OBJECT-Struktur gespeichert. Das folgende Diagramm veranschaulicht die Funktionszeiger, die der Parporttreiber in seiner DriverEntry-Funktion bereitgestellt hat. Die von parport bereitgestellten Funktionszeiger sind schattiert.

Diagramm von Funktionszeigern in einer Treiber-Objekt-Struktur.

Einfachere Verwendung von Treiberpaaren

Als Treiberentwickler innerhalb und außerhalb von Microsoft Erfahrungen mit dem Windows-Treibermodell (WDM) sammelten, haben sie im Laufe einer Zeit einiges über Die Dispatchfunktionen erkannt:

  • Dispatch-Funktionen sind größtenteils boilerplate. Beispielsweise ist ein Großteil des Codes in der Dispatchfunktion für IRP_MJ_PNP für alle Treiber identisch. Es ist nur ein kleiner Teil des Plug & Play (PnP)-Codes, der für einen einzelnen Treiber spezifisch ist, der eine einzelne Hardware steuert.
  • Dispatch-Funktionen sind kompliziert und schwer richtig zu bekommen. Die Implementierung von Features wie Threadsynchronisierung, IRP-Warteschlangen und IRP-Abbruch stellt eine Herausforderung dar und erfordert ein tiefes Verständnis der Funktionsweise des Betriebssystems.

Um Dies für Treiberentwickler zu vereinfachen, hat Microsoft mehrere technologiespezifische Treibermodelle erstellt. Auf den ersten Blick scheinen sich die technologiespezifischen Modelle deutlich voneinander zu unterscheiden, doch ein genauerer Blick zeigt, dass viele von ihnen auf diesem Paradigma basieren:

  • Der Treiber ist in zwei Teile unterteilt: eine, die die allgemeine Verarbeitung übernimmt, und eine, die die verarbeitungsspezifisch für ein bestimmtes Gerät verarbeitet.
  • Das allgemeine Stück wurde von Microsoft geschrieben.
  • Das jeweilige Stück kann von Microsoft oder einem unabhängigen Hardwareanbieter geschrieben werden.

Angenommen, die Unternehmen Proseware und Contoso stellen beide einen Spielzeugroboter her, der einen WDM-Treiber erfordert. Angenommen, Microsoft bietet einen allgemeinen Robotertreiber namens GeneralRobot.sys. Proseware und Contoso können jeweils kleine Treiber schreiben, die die Anforderungen ihrer spezifischen Roboter erfüllen. Beispielsweise könnte Proseware ProsewareRobot.sys schreiben, und das Treiberpaar (ProsewareRobot.sys, GeneralRobot.sys) könnte zu einem einzelnen WDM-Treiber kombiniert werden. Ebenso kann das Treiberpaar (ContosoRobot.sys, GeneralRobot.sys) zu einem einzelnen WDM-Treiber kombiniert werden. In seiner allgemeinsten Form ist die Idee, dass Sie Treiber mithilfe von (specific.sys, general.sys) -Paaren erstellen können.

Funktionszeiger in Treiberpaaren

In einem (specific.sys, general.sys)-Paar lädt Windows specific.sys und ruft die DriverEntry-Funktion auf. Die DriverEntry-Funktion von specific.sys empfängt einen Zeiger auf eine DRIVER_OBJECT-Struktur . Normalerweise würden Sie davon ausgehen , dass DriverEntry mehrere Elemente des MajorFunction-Arrays mit Zeigern auf die Verteilung von Funktionen füllt. Außerdem würden Sie erwarten , dass DriverEntry das Unload-Element (und möglicherweise das StartIo-Element ) der DRIVER_OBJECT-Struktur und das AddDevice-Element der Treiberobjekterweiterung eingibt. In einem Treiberpaarmodell macht DriverEntry dies jedoch nicht unbedingt. Stattdessen übergibt die DriverEntry-Funktion von specific.sys die DRIVER_OBJECT-Struktur an eine Initialisierungsfunktion, die von general.sys implementiert wird. Das folgende Codebeispiel zeigt, wie die Initialisierungsfunktion im Paar (ProsewareRobot.sys, GeneralRobot.sys) aufgerufen werden kann.

PVOID g_ProsewareRobottCallbacks[3] = {DeviceControlCallback, PnpCallback, PowerCallback};

// DriverEntry function in ProsewareRobot.sys
NTSTATUS DriverEntry (DRIVER_OBJECT *DriverObject, PUNICODE_STRING RegistryPath)
{
   // Call the initialization function implemented by GeneralRobot.sys.
   return GeneralRobotInit(DriverObject, RegistryPath, g_ProsewareRobottCallbacks);
}

Die Initialisierungsfunktion in GeneralRobot.sys schreibt Funktionszeiger auf die entsprechenden Member der DRIVER_OBJECT-Struktur (und deren Erweiterung) und die entsprechenden Elemente des MajorFunction-Arrays . Wenn der E/A-Manager ein IRP an das Treiberpaar sendet, wird der IRP zuerst an eine von GeneralRobot.sys implementierte Dispatchfunktion weitergeleitet. Wenn GeneralRobot.sys die IRP selbst verarbeiten kann, muss der spezifische Treiber, ProsewareRobot.sys, nicht beteiligt sein. Wenn GeneralRobot.sys einige, aber nicht alle der IRP-Verarbeitung verarbeiten kann, erhält sie Hilfe von einer der Rückruffunktionen, die von ProsewareRobot.sys implementiert werden. GeneralRobot.sys empfängt Zeiger auf die ProsewareRobot-Rückrufe im GeneralRobotInit-Aufruf.

Nach der Rückgabe von DriverEntry wird irgendwann ein Gerätestapel für den Proseware Robot-Geräteknoten erstellt. Der Gerätestapel könnte wie folgt aussehen.

Diagramm des Proseware-Robotergeräteknotens mit drei Geräteobjekten im Gerätestapel: afterthought.sys (Filter do), prosewarerobot.sys, generalrobot.sys (fdo) und pci.sys (pdo).

Wie im vorherigen Diagramm gezeigt, enthält der Gerätestapel für Proseware Robot drei Geräteobjekte. Das oberste Geräteobjekt ist ein Filtergeräteobjekt (Filter DO), das dem Filtertreiber AfterThought.sys zugeordnet ist. Das mittlere Geräteobjekt ist ein funktionales Geräteobjekt (Functional Device Object, FDO), das dem Treiberpaar zugeordnet ist (ProsewareRobot.sys, GeneralRobot.sys). Das Treiberpaar dient als Funktionstreiber für den Gerätestapel. Das untere Geräteobjekt ist ein physisches Geräteobjekt (PDO), das Pci.sys zugeordnet ist.

Beachten Sie, dass das Treiberpaar nur eine Ebene im Gerätestapel belegt und nur einem Geräteobjekt zugeordnet ist: der FDO. Wenn GeneralRobot.sys einen IRP verarbeitet, ruft er möglicherweise ProsewareRobot.sys auf, um Hilfe zu erhalten. Dies ist jedoch nicht dasselbe wie die Weitergabe der Anforderung im Gerätestapel. Das Treiberpaar bildet einen einzelnen WDM-Treiber, der sich auf einer Ebene im Gerätestapel befindet. Das Treiberpaar schließt entweder die IRP ab oder übergibt es im Gerätestapel an die PDO, die Pci.sys zugeordnet ist.

Beispiel für ein Treiberpaar

Angenommen, Sie verfügen über ein drahtloses Netzwerk Karte auf Ihrem Laptop, und wenn Sie in Geräte-Manager suchen, stellen Sie fest, dass netwlv64.sys der Treiber für das Netzwerk Karte ist. Sie können die Debuggererweiterung !drvobj verwenden, um die Funktionszeiger auf netwlv64.sys zu überprüfen.

1: kd> !drvobj netwlv64 2
Driver object (fffffa8002e5f420) is for:
 \Driver\netwlv64
DriverEntry:   fffff8800482f064 netwlv64!GsDriverEntry
DriverStartIo: 00000000 
DriverUnload:  fffff8800195c5f4 ndis!ndisMUnloadEx
AddDevice:     fffff88001940d30 ndis!ndisPnPAddDevice
Dispatch routines:
[00] IRP_MJ_CREATE                      fffff880018b5530 ndis!ndisCreateIrpHandler
[01] IRP_MJ_CREATE_NAMED_PIPE           fffff88001936f00 ndis!ndisDummyIrpHandler
[02] IRP_MJ_CLOSE                       fffff880018b5870 ndis!ndisCloseIrpHandler
[03] IRP_MJ_READ                        fffff88001936f00 ndis!ndisDummyIrpHandler
[04] IRP_MJ_WRITE                       fffff88001936f00 ndis!ndisDummyIrpHandler
[05] IRP_MJ_QUERY_INFORMATION           fffff88001936f00 ndis!ndisDummyIrpHandler
[06] IRP_MJ_SET_INFORMATION             fffff88001936f00 ndis!ndisDummyIrpHandler
[07] IRP_MJ_QUERY_EA                    fffff88001936f00 ndis!ndisDummyIrpHandler
[08] IRP_MJ_SET_EA                      fffff88001936f00 ndis!ndisDummyIrpHandler
[09] IRP_MJ_FLUSH_BUFFERS               fffff88001936f00 ndis!ndisDummyIrpHandler
[0a] IRP_MJ_QUERY_VOLUME_INFORMATION    fffff88001936f00 ndis!ndisDummyIrpHandler
[0b] IRP_MJ_SET_VOLUME_INFORMATION      fffff88001936f00 ndis!ndisDummyIrpHandler
[0c] IRP_MJ_DIRECTORY_CONTROL           fffff88001936f00 ndis!ndisDummyIrpHandler
[0d] IRP_MJ_FILE_SYSTEM_CONTROL         fffff88001936f00 ndis!ndisDummyIrpHandler
[0e] IRP_MJ_DEVICE_CONTROL              fffff8800193696c ndis!ndisDeviceControlIrpHandler
[0f] IRP_MJ_INTERNAL_DEVICE_CONTROL     fffff880018f9114 ndis!ndisDeviceInternalIrpDispatch
[10] IRP_MJ_SHUTDOWN                    fffff88001936f00 ndis!ndisDummyIrpHandler
[11] IRP_MJ_LOCK_CONTROL                fffff88001936f00 ndis!ndisDummyIrpHandler
[12] IRP_MJ_CLEANUP                     fffff88001936f00 ndis!ndisDummyIrpHandler
[13] IRP_MJ_CREATE_MAILSLOT             fffff88001936f00 ndis!ndisDummyIrpHandler
[14] IRP_MJ_QUERY_SECURITY              fffff88001936f00 ndis!ndisDummyIrpHandler
[15] IRP_MJ_SET_SECURITY                fffff88001936f00 ndis!ndisDummyIrpHandler
[16] IRP_MJ_POWER                       fffff880018c35e8 ndis!ndisPowerDispatch
[17] IRP_MJ_SYSTEM_CONTROL              fffff880019392c8 ndis!ndisWMIDispatch
[18] IRP_MJ_DEVICE_CHANGE               fffff88001936f00 ndis!ndisDummyIrpHandler
[19] IRP_MJ_QUERY_QUOTA                 fffff88001936f00 ndis!ndisDummyIrpHandler
[1a] IRP_MJ_SET_QUOTA                   fffff88001936f00 ndis!ndisDummyIrpHandler
[1b] IRP_MJ_PNP                         fffff8800193e518 ndis!ndisPnPDispatch

In der Debuggerausgabe sehen Sie, dass netwlv64.sys GsDriverEntry implementiert, den Einstiegspunkt für den Treiber. GsDriverEntry, das beim Erstellen des Treibers automatisch generiert wurde, führt eine Initialisierung durch und ruft dann DriverEntry auf, das vom Treiberentwickler geschrieben wurde.

In diesem Beispiel implementiert netwlv64.sys DriverEntry, aber ndis.sys implementiert AddDevice, Unload und mehrere Dispatchfunktionen. Netwlv64.sys wird als NDIS-Miniporttreiber bezeichnet, und ndis.sys wird als NDIS-Bibliothek bezeichnet. Zusammen bilden die beiden Module ein Paar (NDIS-Miniport, NDIS-Bibliothek).

Dieses Diagramm zeigt den Gerätestapel für das drahtlose Netzwerk Karte. Beachten Sie, dass das Treiberpaar (netwlv64.sys, ndis.sys) nur eine Ebene im Gerätestapel einnimmt und nur einem Geräteobjekt zugeordnet ist: dem FDO.

Diagramm des Funknetzwerks Karte Gerätestapels mit netwlv64.sys, ndis.sys als Treiberpaar, das dem fdo und pci.sys zugeordnet ist, das dem pdo zugeordnet ist.

Verfügbare Treiberpaare

Die verschiedenen technologiespezifischen Treibermodelle verwenden eine Vielzahl von Namen für die spezifischen und allgemeinen Teile eines Treiberpaars. In vielen Fällen weist der spezifische Teil des Paars das Präfix "mini" auf. Hier sind einige (spezifische, allgemeine) Paare, die verfügbar sind:

  • (Anzeige-Miniporttreiber, Anzeigeporttreiber)
  • (Audio-Miniporttreiber, Audioporttreiber)
  • (Speicher-Miniporttreiber, Speicherporttreiber)
  • (Akku-Miniklasse-Treiber, Akkuklassentreiber)
  • (HID-Minitreiber, HID-Klassentreiber)
  • (Changer miniclass driver, changer port driver)
  • (NDIS-Miniporttreiber, NDIS-Bibliothek)

Hinweis Wie Sie in der Liste sehen können, verwenden mehrere Modelle den Begriff Klassentreiber für den allgemeinen Teil eines Treiberpaars. Diese Art von Klassentreiber unterscheidet sich von einem eigenständigen Klassentreiber und von einem Klassenfiltertreiber.

Konzepte für alle Treiberentwickler

Geräteknoten und Gerätestapel

Treiberstapel