Zugreifen auf Datenpuffer in WDF-Treibern (KMDF oder UMDF)

Wenn ein WDF-Treiber (Windows Driver Frameworks) eine Lese-, Schreib- oder Geräte-E/A-Steuerungsanforderung empfängt, enthält das Anforderungsobjekt entweder einen Eingabepuffer, einen Ausgabepuffer oder beides.

Eingabepuffer enthalten Informationen, die der Treiber benötigt. Bei Schreibanforderungen sind diese Informationen in der Regel Daten, die ein Funktionstreiber an ein Gerät senden muss. Bei Geräte-E/A-Steuerelementanforderungen kann ein Eingabepuffer Informationen enthalten, die den Typ des Vorgangs angeben, den der Treiber ausführen muss.

Ausgabepuffer empfangen Informationen vom Treiber. Bei Leseanforderungen handelt es sich bei diesen Informationen in der Regel um Daten, die ein Funktionstreiber von einem Gerät empfängt. Für Geräte-E/A-Steuerungsanforderungen kann ein Ausgabepuffer status oder andere Informationen empfangen, die durch den E/A-Steuerungscode der Anforderung angegeben wurden.

Die Technik, die Ihr Treiber für den Zugriff auf die Datenpuffer einer Anforderung verwendet, hängt von der Methode des Treibers für den Zugriff auf Datenpuffer für ein Gerät ab. Es gibt drei Zugriffsmethoden:

  • Gepufferte E/A. Der E/A-Manager erstellt Zwischenpuffer, die er mit dem Treiber teilt.
  • Direkte E/A. Der E/A-Manager sperrt den Pufferspeicher in den physischen Arbeitsspeicher und gewährt dem Treiber dann direkten Zugriff auf den Pufferspeicher.
  • Weder gepuffert noch direkte E/A. Der E/A-Manager stellt dem Treiber die virtuellen Adressen des Pufferspeichers der Anforderung zur Verfügung. Der E/A-Manager überprüft nicht den Pufferspeicher der Anforderung, daher muss der Treiber überprüfen, ob auf den Pufferspeicher zugegriffen werden kann, und den Pufferspeicher im physischen Arbeitsspeicher sperren.

Ein kmDF-Treiber (Kernel-Mode Driver Framework) kann eine der drei Zugriffsmethoden verwenden. Ein User-Mode Driver Framework(UMDF)-Treiber kann gepufferte oder direkte E/A für Lese-, Schreib- und IOCTL-Anforderungen verwenden und Anforderungen konvertieren, die die METHOD_NEITHER-Methode angeben.

Angeben der Pufferzugriffsmethode

KMDF-Treiber

Für Lese- und Schreibanforderungen müssen alle Treiber in einem Treiberstapel die gleiche Methode für den Zugriff auf die Puffer eines Geräts verwenden, mit Ausnahme des Treibers der höchsten Ebene, der die Methode "weder" verwenden kann, unabhängig davon, welche Methode von niedrigeren Treibern verwendet wird.

Ab Version 1.13 gibt ein KMDF-Treiber die Zugriffsmethode für alle Lese- und Schreibanforderungen eines Geräts an, indem er WdfDeviceInitSetIoTypeEx für jedes Gerät aufruft. Wenn ein Treiber beispielsweise die gepufferte E/A-Methode für eines seiner Geräte angibt, verwendet der E/A-Manager die gepufferte E/A-Methode, wenn Lese- und Schreibanforderungen an den Treiber für dieses Gerät gesendet werden.

Für Geräte-E/A-Steuerelementanforderungen enthält der E/A-Steuerungscode (IOCTL) Bits, die die Pufferzugriffsmethode angeben. Daher muss ein KMDF-Treiber keine Aktion ausführen, um eine Puffermethode für IOCTLs auszuwählen. Weitere Informationen zu IOCTLs finden Sie unter Definieren von E/A-Steuerungscodes. Im Gegensatz zu Lese- und Schreibanforderungen müssen alle IOCTLs eines Geräts nicht die gleiche Zugriffsmethode angeben.

UMDF-Treiber

Ein UMDF-Treiber gibt Einstellungen für die Zugriffsmethode an, die das Framework für Lese- und Schreibanforderungen sowie Geräte-E/A-Steuerungsanforderungen verwendet. Die Werte, die ein UMDF-Treiber bereitstellt, sind nur Einstellungen und sind nicht garantiert, dass sie vom Framework verwendet werden. Weitere Informationen finden Sie unter Verwalten von Pufferzugriffsmethoden in UMDF-Treibern.

Ein UMDF-Treiber gibt die Zugriffsmethode für alle Lese-, Schreib- und IOCTL-Anforderungen eines Geräts an, indem WdfDeviceInitSetIoTypeEx für jedes Gerät aufgerufen wird. Wenn ein Treiber beispielsweise die gepufferte E/A-Methode für eines seiner Geräte angibt, verwendet das Framework die gepufferte E/A-Methode, wenn Lese-, Schreib- und IOCTL-Anforderungen an den Treiber für dieses Gerät gesendet werden.

Beachten Sie den Unterschied in der Pufferzugriffstechnik für IOCTLs zwischen KMDF und UMDF. KMDF-Treiber geben keine Pufferzugriffsmethode für IOCTLs an, während UMDF-Treiber die Pufferzugriffsmethode für IOCTLs angeben.

Wenn ein WDF-Treiber den Puffer einer E/A-Anforderung mithilfe einer Technik beschreibt, die für die E/A-Methode, die ein E/A-Ziel verwendet, falsch ist, korrigiert das Framework die Pufferbeschreibung. Wenn ein Treiber beispielsweise eine MDL verwendet, um einen Puffer zu beschreiben, den er an WdfIoTargetSendReadSynchronously übergibt, und wenn das E/A-Ziel gepufferte E/A-Vorgänge verwendet (was erfordert, dass Puffer mithilfe virtueller Adressen anstelle von MDLs angegeben werden), konvertiert das Framework die Pufferbeschreibung von einer MDL in eine virtuelle Adresse und länge. Es ist jedoch effizienter, wenn Ihr Treiber Puffer im richtigen Format angibt.

Informationen zu Frameworkspeicherobjekten, Suchlisten, MDLs und lokalen Puffern finden Sie unter Verwenden von Speicherpuffern.

Informationen zum Zeitpunkt des Löschens von Speicherpuffern finden Sie unter Lebenszyklus des Speicherpuffers.

Zugreifen auf Datenpuffer für gepufferte E/A

Wenn Ihr Treiber gepufferte E/A verwendet, ändert sich sein Verhalten abhängig vom Typ der Datenanforderung und davon, ob er KMDF oder UMDF verwendet.

KMDF-Treiber

Wenn ein KMDF-Treiber gepufferte E/A verwendet, erstellt der E/A-Manager einen Zwischenpuffer, auf den der Treiber für jeden Anforderungstyp zugreifen kann. Die Vorgehensweise ist wie folgt:

  • Schreibanforderungen. Der E/A-Manager überträgt Eingabeinformationen aus dem Eingabepuffer der aufrufenden App, bevor er den Treiberstapel aufruft. Anschließend liest der KMDF-Treiber Eingabeinformationen aus dem Zwischenpuffer und schreibt sie auf das Gerät.
  • Leseanforderungen. Der KMDF-Treiber liest Informationen vom Gerät und speichert sie im Zwischenpuffer. Anschließend kopiert der E/A-Manager die Ausgabedaten aus dem Zwischenpuffer in den Ausgabepuffer der App.
  • Geräte-E/A-Steuerungsanforderungen. Der KMDF-Treiber liest oder schreibt Daten für diese Anforderung an oder aus dem Zwischenpuffer.

UMDF-Treiber

Wenn ein UMDF-Treiber gepufferte E/A verwendet, erstellt der Treiberhostprozess je nach Anforderungstyp einen oder zwei Zwischenpuffer. Die Vorgehensweise ist wie folgt:

  • Schreibanforderungen. Das Framework erstellt einen Puffer, überträgt Eingabeinformationen aus dem Eingabepuffer der aufrufenden App und ruft dann den Treiberstapel auf. Der UMDF-Treiber liest Eingabeinformationen aus dem Zwischenpuffer und schreibt sie auf das Gerät.
  • Leseanforderungen. Ein UMDF-Treiber liest Informationen von einem Gerät und speichert sie in einem Puffer, den das Framework erstellt hat. Der Treiberhostprozess kopiert die Ausgabedaten aus dem Zwischenpuffer in den Ausgabepuffer der App.
  • Geräte-E/A-Steuerungsanforderungen. Das Framework erstellt zwei Puffer, die den Eingabe- und Ausgabepuffern des IOCTL entsprechen, auf die der Treiber zugreifen kann. Das Framework kopiert die Eingabeinformationen aus dem IOCTL in den neuen Zwischenpuffer und stellt sie dem Treiber zur Verfügung. Das Framework kopiert nicht den Inhalt des Ausgabepuffers, sodass der Treiber nicht versuchen sollte, daraus zu lesen (andernfalls wird am Ende Mülldaten gelesen). Alle Daten, die der Treiber in den Ausgabepuffer schreibt, werden zurück in den ursprünglichen IOCTL-Puffer kopiert und nach erfolgreichem Abschluss der E/A-Anforderung an die App zurückgegeben. Beachten Sie, dass alle Daten, die der Treiber in den Eingabepuffer schreibt, verworfen und nicht an die aufrufende App zurückgegeben werden.

Um ein Handle für ein Frameworkspeicherobjekt abzurufen, das den Puffer darstellt, rufen sowohl KMDF- als auch UMDF-Treiber WdfRequestRetrieveInputMemory oder WdfRequestRetrieveOutputMemory auf, je nachdem, ob es sich um eine Lese- oder Schreibanforderung handelt. Der Treiber kann dann einen Zeiger auf den Puffer abrufen, indem er WdfMemoryGetBuffer aufruft. Zum Lesen und Schreiben des Puffers ruft der Treiber WdfMemoryCopyFromBuffer oder WdfMemoryCopyToBuffer auf.

Um die virtuelle Adresse und länge des Puffers abzurufen, ruft der Treiber WdfRequestRetrieveInputBuffer oder WdfRequestRetrieveOutputBuffer auf.

Um eine Speicherbeschreibungsliste (Memory Descriptor List, MDL) für den Puffer zuzuweisen und zu erstellen, ruft ein KMDF-Treiber WdfRequestRetrieveInputWdmMdl oder WdfRequestRetrieveOutputWdmMdl auf.

Zugreifen auf Datenpuffer für direkte E/A-Vorgänge

KMDF-Treiber

Wenn Ihr Treiber direkte E/A verwendet, überprüft der E/A-Manager die Barrierefreiheit des Pufferbereichs, den der Absender der E/A-Anforderung (in der Regel eine Anwendung im Benutzermodus) angegeben hat, sperrt den Pufferspeicher in den physischen Arbeitsspeicher und gewährt dem Treiber dann direkten Zugriff auf den Pufferspeicher.

UMDF-Treiber

Wenn Ihr Treiber eine Einstellung für direkte E/A angegeben hat und alle UMDF-Anforderungen für direkte E/A erfüllt wurden (siehe Verwalten von Pufferzugriffsmethoden in UMDF-Treibern), ordnet das Framework den Vom E/A-Manager empfangenen Speicherpuffer direkt in den Hostprozessadressraum des Treibers zu und ermöglicht dem Treiber somit direkten Zugriff auf den Pufferspeicher.

Um ein Handle für ein Frameworkspeicherobjekt abzurufen, das den Pufferspeicher darstellt, ruft der Treiber WdfRequestRetrieveInputMemory oder WdfRequestRetrieveOutputMemory auf. Der Treiber kann dann einen Zeiger auf den Puffer abrufen, indem er WdfMemoryGetBuffer aufruft. Zum Lesen und Schreiben des Puffers ruft der Treiber WdfMemoryCopyFromBuffer oder WdfMemoryCopyToBuffer auf.

Um die virtuelle Adresse und länge des Pufferraums abzurufen, ruft der Treiber WdfRequestRetrieveInputBuffer oder WdfRequestRetrieveOutputBuffer auf.

Wenn die Treiber eines Geräts direkte E/A verwenden, beschreibt der E/A-Manager Puffer mithilfe von MDLs. Um einen Zeiger auf die MDL eines Puffers abzurufen, ruft ein KMDF-Treiber WdfRequestRetrieveInputWdmMdl oder WdfRequestRetrieveOutputWdmMdl auf. Ein UMDF-Treiber kann nicht auf MDLs zugreifen.

Zugreifen auf Datenpuffer für weder gepufferte noch direkte E/A-Vorgänge

KMDF-Treiber

Wenn Ihr Treiber die Pufferzugriffsmethode verwendet, die weder als gepufferte E/A-Methode noch als direkte E/A-Methode (oder kurz die "weder"-Methode) bezeichnet wird, stellt der E/A-Manager dem Treiber einfach die virtuellen Adressen zur Verfügung, die der Absender der E/A-Anforderung für den Pufferspeicher der Anforderung angegeben hat. Der E/A-Manager überprüft den Pufferspeicher der E/A-Anforderung nicht. Daher muss der Treiber überprüfen, ob auf den Pufferspeicher zugegriffen werden kann, und den Pufferspeicher im physischen Arbeitsspeicher sperren.

Auf die vom E/A-Manager bereitgestellten virtuellen Adressen kann nur im Prozesskontext des Absenders der E/A-Anforderung zugegriffen werden. Nur der Treiber der höchsten Ebene im Treiberstapel wird garantiert im Prozesskontext des Absenders ausgeführt.

Um Zugriff auf den Pufferraum einer E/A-Anforderung zu erhalten, muss der Treiber der höchsten Ebene eine EvtIoInCallerContext-Rückruffunktion bereitstellen. Das Framework ruft diese Rückruffunktion jedes Mal auf, wenn es eine E/A-Anforderung für den Treiber empfängt.

Wenn die Pufferzugriffsmethode einer Anforderung "Weder" lautet, muss ein KMDF-Treiber für jeden Puffer Folgendes tun:

  1. Rufen Sie WdfRequestRetrieveUnsafeUserInputBuffer oder WdfRequestRetrieveUnsafeUserOutputBuffer auf, um die virtuelle Adresse des Puffers abzurufen.

  2. Rufen Sie WdfRequestProbeAndLockUserBufferForRead oder WdfRequestProbeAndLockUserBufferForWrite auf, um den Puffer zu testen und zu sperren und ein Handle für ein Frameworkspeicherobjekt für den Puffer abzurufen.

  3. Speichern Sie die Speicherobjekthandles im Kontextbereich der Anforderung.

  4. Rufen Sie WdfDeviceEnqueueRequest auf, wodurch die Anforderung an das Framework zurückgegeben wird.

Das Framework fügt die Anforderung anschließend einer der E/A-Warteschlangen des Treibers hinzu. Wenn der Treiber Anforderungshandler bereitgestellt hat, ruft das Framework schließlich den entsprechenden Anforderungshandler auf.

Der Anforderungshandler kann die Speicherobjekthandles der Anforderung aus dem Kontextbereich der Anforderung abrufen. Der Treiber kann die Handles an WdfMemoryGetBuffer übergeben, um die Adresse des Puffers abzurufen.

Gelegentlich muss ein Treiber der höchsten Ebene die vorherigen Schritte ausführen, um auf einen Benutzermoduspuffer zuzugreifen, auch wenn der Treiber nicht die Zugriffsmethode "Weder" verwendet. Angenommen, der Treiber verwendet gepufferte E/A. Ein E/A-Steuerelementcode, der die gepufferte Zugriffsmethode verwendet, kann eine Struktur, die einen eingebetteten Zeiger enthält, an einen Benutzermoduspuffer übergeben. In einem solchen Fall muss der Treiber eine EvtIoInCallerContext-Rückruffunktion bereitstellen, die die Zeiger aus der Struktur extrahiert und dann die vorherigen Schritte 2 bis 4 verwendet.

UMDF-Treiber

UMDF unterstützt weder gepufferte noch direkte E/A-Typpuffer, sodass ein UMDF-Treiber diesen Puffertyp nie direkt verarbeiten muss.

Wenn das Framework jedoch solche Puffer zum Lesen oder Schreiben vom E/A-Manager empfängt, stellt es diese je nach der vom Treiber ausgewählten Zugriffsmethode als gepufferte E/A oder direkte E/A für einen UMDF-Treiber zur Verfügung. Wenn das Framework eine IOCTL empfängt, die die Puffermethode "neither" angibt, kann es optional die Pufferzugriffsmethode der IOCTL-Anforderung basierend auf einer INF-Direktive in gepufferte E/A oder direkte E/A konvertieren. Weitere Informationen finden Sie unter Verwalten von Pufferzugriffsmethoden in UMDF-Treibern .