Verwenden von Framework-Arbeitselementen

Ein Arbeitselement ist eine Aufgabe, die ein Treiber in einer EvtWorkItem-Ereignisrückruffunktion ausführt. Diese Funktionen werden asynchron unter IRQL = PASSIVE_LEVEL im Kontext eines Systemworkerthreads ausgeführt.

Frameworkbasierte Treiber verwenden häufig Arbeitselemente, wenn eine EvtInterruptDpc - oder EvtDpcFunc-Funktion , die unter IRQL = DISPATCH_LEVEL ausgeführt wird, eine zusätzliche Verarbeitung unter IRQL = PASSIVE_LEVEL ausführen muss.

Anders ausgedrückt: Ein Treiber kann Arbeitselemente verwenden, wenn eine Funktion, die unter IRQL = DISPATCH_LEVEL ausgeführt wird, eine Funktion aufrufen muss, die nur unter IRQL = PASSIVE_LEVEL aufgerufen werden kann.

In der Regel erstellt die Rückruffunktion EvtInterruptDpc oder EvtDpcFunc eines Treibers ein Arbeitselementobjekt und fügt es der Arbeitselementwarteschlange des Systems hinzu. Anschließend entfernt ein Systemworkerthread die Warteschlange für das Objekt und ruft die EvtWorkItem-Rückruffunktion des Arbeitselements auf.

Beispieltreiber, die Arbeitselemente verwenden

Beispiele für frameworkbasierte Treiber , die Arbeitselemente verwenden, sind 1394, AMCC5933, PCIDRV und Toaster.

Einrichten eines Arbeitselements

Um ein Arbeitselement einzurichten, muss Ihr Treiber:

  1. Erstellen Sie das Arbeitselement.

    Ihr Treiber ruft WdfWorkItemCreate auf, um ein Arbeitselementobjekt zu erstellen und eine EvtWorkItem-Rückruffunktion zu identifizieren, die das Arbeitselement verarbeitet.

  2. Speichern Sie Informationen über das Arbeitselement.

    In der Regel verwenden Treiber den Kontextspeicher des Arbeitselementobjekts, um Informationen zu der Aufgabe zu speichern, die von der EvtWorkItem-Rückruffunktion ausgeführt werden soll. Wenn die EvtWorkItem-Rückruffunktion aufgerufen wird, kann sie die Informationen abrufen, indem sie auf diesen Kontextspeicher zugreift. Informationen zum Zuordnen und Zugreifen auf Kontextspeicher finden Sie unter Framework Object Context Space.

  3. Fügen Sie das Arbeitselement der Arbeitselementwarteschlange des Systems hinzu.

    Ihr Treiber ruft WdfWorkItemEnqueue auf, wodurch das Arbeitselement des Treibers der Arbeitselementwarteschlange hinzugefügt wird.

Wenn Ihr Treiber WdfWorkItemCreate aufruft, muss er entweder ein Handle für ein Frameworkgeräteobjekt oder ein Frameworkwarteschlangenobjekt bereitstellen. Wenn das System dieses Objekt löscht, löscht es auch alle vorhandenen Arbeitselemente, die dem -Objekt zugeordnet sind. Das Arbeitselementobjekt wird verworfen, und der zugehörige Arbeitselementrückruf wird bereinigt, bevor der EvtCleanupCallback-Rückruf des übergeordneten Objekts aufgerufen wird.

Weitere Informationen zu den Bereinigungsregeln für eine Frameworkobjekthierarchie finden Sie unter Framework Object Life Cycle.

Verwenden der Work-Item-Rückruffunktion

Nachdem das Arbeitselement der Arbeitselementwarteschlange hinzugefügt wurde, verbleibt es in der Warteschlange, bis ein Systemarbeitsthread verfügbar ist. Der Systemarbeitsthread entfernt das Arbeitselement aus der Warteschlange und ruft dann die EvtWorkItem-Rückruffunktion des Treibers auf, wobei das Arbeitselementobjekt als Eingabe übergeben wird.

In der Regel führt die EvtWorkItem-Rückruffunktion die folgenden Schritte aus:

  1. Ruft vom Treiber bereitgestellte Informationen über das Arbeitselement ab, indem auf den Kontextspeicher des Arbeitselementobjekts zugegriffen wird.

  2. Führt die von Ihnen angegebene Aufgabe aus. Bei Bedarf kann die Rückruffunktion WdfWorkItemGetParentObject aufrufen, um das übergeordnete Objekt des Arbeitselements zu bestimmen.

  3. Ruft WdfObjectDelete auf, um das Arbeitselementobjekt zu löschen. Wenn der Treiber das Arbeitselement erneut in die Warteschlange stellt, gibt an, dass das Handle für das Arbeitselement jetzt zur Wiederverwendung verfügbar ist.

Die Aufgabe, die von der Rückruffunktion der einzelnen Arbeitselemente ausgeführt wird, muss relativ kurz sein. Das Betriebssystem bietet eine begrenzte Anzahl von System-Workerthreads, sodass Ihr Treiber die Systemleistung beeinträchtigen kann, wenn er Rückruffunktionen für Arbeitselemente verwendet, um zeitaufwändige Aufgaben auszuführen.

Erstellen und Löschen von Arbeitselementen

Treiber können eine der folgenden beiden Techniken verwenden, um Arbeitselemente zu erstellen und zu löschen:

  • Verwenden Sie jedes Arbeitselement einmal: Erstellen Sie das Arbeitselement, wenn Sie es benötigen, und löschen Sie es sofort, nachdem es verwendet wurde.

    Diese Technik ist nützlich für Treiber, die eine geringe Anzahl von Arbeitselementen nur selten (seltener als einmal pro Minute) verwenden müssen.

    Beispielsweise kann die EvtInterruptDpc-Rückruffunktion eines Treibers WdfWorkItemCreate und dann WdfWorkItemEnqueue aufrufen, und die EvtWorkItem-Rückruffunktion des Arbeitselements kann WdfObjectDelete aufrufen.

    Wenn Ihr Treiber diesem Szenario folgt und seine EvtInterruptDpc-Rückruffunktion einen STATUS_INSUFFICIENT_RESOURCES Rückgabewert von WdfWorkItemCreate empfängt, muss der Treiber in der Lage sein, die erforderliche Arbeit zu verschieben, bis Systemressourcen (in der Regel Arbeitsspeicher) verfügbar sind.

  • Erstellen Sie eine oder mehrere Arbeitselemente, die Ihr Treiber bei Bedarf erneut in die Warteschlange stellt.

    Diese Technik ist nützlich für Treiber, die Arbeitselemente häufig (öfter als einmal pro Minute) verwenden, oder wenn die EvtInterruptDpc-Rückruffunktion Ihres Treibers einen STATUS_INSUFFICIENT_RESOURCES Rückgabewert von WdfWorkItemCreate nicht einfach verarbeiten kann.

    Das System ordnet einem Arbeitselement erst einen Workerthread zu, wenn der Treiber WdfWorkItemEnqueue aufruft. Obwohl Systemworkerthreads eine begrenzte Ressource sind, verbraucht das Erstellen von Arbeitselementen während der Initialisierung eines Geräts eine geringe Menge an Arbeitsspeicher, wirkt sich jedoch nicht auf die Systemleistung aus.

    In den folgenden Schritten wird ein mögliches Szenario beschrieben:

    1. Die Rückruffunktion EvtDriverDeviceAdd eines Treibers ruft WdfWorkItemCreate auf, um ein Arbeitselementhandle abzurufen.
    2. Die EvtInterruptDpc-Rückruffunktion des Treibers erstellt eine Liste von Aktionen, die die EvtWorkItem-Rückruffunktion ausführen muss, und ruft dann WdfWorkItemEnqueue auf, wobei das Handle aus Schritt 1 verwendet wird.
    3. Die EvtWorkItem-Rückruffunktion des Treibers führt die Liste der Aktionen aus und legt ein Flag fest, um anzugeben, dass die Rückruffunktion ausgeführt wurde.

    Anschließend muss jedes Mal, wenn die EvtInterruptDpc-Rückruffunktion des Treibers aufgerufen wird, ermittelt werden, ob die EvtWorkItem-Rückruffunktion ausgeführt wurde. Wenn die EvtWorkItem-Rückruffunktion nicht ausgeführt wurde, ruft die EvtInterruptDpc-RückruffunktionWdfWorkItemEnqueue nicht auf, da sich das Arbeitselement weiterhin in der Warteschlange befindet. In diesem Fall aktualisiert die Rückruffunktion EvtInterruptDpc nur die Liste der Aktionen für die EvtWorkItem-Rückruffunktion .

    Jedes Arbeitselement ist einem Gerät oder einer Warteschlange zugeordnet. Wenn das zugeordnete Gerät oder die zugeordnete Warteschlange entfernt wird, löscht das Framework alle zugeordneten Arbeitselemente. Wenn Sie also diese Technik verwenden, muss der Treiber WdfObjectDelete nicht aufrufen.

Einige Treiber müssen möglicherweise WdfWorkItemFlush aufrufen, um ihre Arbeitselemente aus der Arbeitselementwarteschlange zu leeren. Ein Beispiel für die Verwendung von WdfWorkItemFlush finden Sie auf der Referenzseite der Methode.

Wenn der Treiber WdfObjectDelete für ein ausstehendes Arbeitselement aufruft, hängt das Ergebnis vom Status des Arbeitselements ab:

Status des Arbeitselements Ergebnis
Erstellt, aber nicht in der Warteschlange Arbeitselement wird sofort bereinigt.
in die Warteschlange eingereiht Der Aufruf von WdfObjectDelete wartet, bis die Ausführung des Arbeitselements abgeschlossen ist, und dann wird das Arbeitselement bereinigt.
Wird ausgeführt Wenn der Treiber WdfObjectDelete aus EvtWorkItem (in demselben Thread) aufruft, wird WdfObjectDelete sofort zurückgegeben. Sobald EvtWorkItem abgeschlossen ist, wird das Arbeitselement bereinigt. Andernfalls wartet WdfObjectDelete auf den Abschluss von EvtWorkItem.