Freigeben über


Verwenden von Timerobjekten

Die folgende Abbildung veranschaulicht die Verwendung eines Benachrichtigungszeitgebers, um ein Timeoutintervall für einen Vorgang einzurichten und dann zu warten, während andere Treiberroutinen eine E/A-Anforderung verarbeiten.

Diagramm, das das Warten auf ein Timerobjekt veranschaulicht.

Wie in der vorherigen Abbildung gezeigt, muss ein Treiber Speicher für das Timerobjekt bereitstellen, das durch einen Aufruf von KeInitializeTimer mit einem Zeiger auf diesen Speicher initialisiert werden muss. Ein Treiber führt diesen Aufruf in der Regel über seine AddDevice-Routine aus.

Im Kontext eines bestimmten Threads, z. B. eines vom Treiber erstellten Threads oder eines Threads, der einen synchronen E/A-Vorgang anfordert, kann der Treiber auf sein Timerobjekt warten, wie in der vorherigen Abbildung gezeigt:

  1. Der Thread ruft KeSetTimer mit einem Zeiger auf das Timerobjekt und einem bestimmten DueTime-Wert auf, ausgedrückt in Einheiten von 100 Nanosekunden. Ein positiver Wert für DueTime gibt einen absoluten Zeitpunkt an, zu dem das Timerobjekt aus der Timerwarteschlange des Kernels entfernt und auf den Signalzustand festgelegt werden soll. Ein negativer Wert für DueTime gibt ein Intervall relativ zur aktuellen Systemzeit an.

    Beachten Sie, dass der Thread (oder die Treiberroutine, die in einem Systemthread ausgeführt wird) einen NULL-Zeiger für das DPC-Objekt übergibt (siehe Abbildung, die die Verwendung von Timer- und DPC-Objekten für eine CustomTimerDpc-Routine veranschaulicht), wenn KeSetTimer aufgerufen wird, wenn er auf das Timerobjekt wartet, anstatt eine CustomTimerDpc-Routine in eine Warteschlange zu stellen.

  2. Der Thread ruft KeWaitForSingleObject mit einem Zeiger auf das Timerobjekt auf, wodurch der Thread in einen Wartezustand versetzt wird, während sich das Timerobjekt in der Timerwarteschlange des Kernels befindet.

  3. Die angegebene DueTime läuft ab.

  4. Der Kernel entfernt das Timerobjekt, legt es auf den Zustand Signaled fest und ändert den Status des Threads von Warten auf bereit.

  5. Der Kernel sendet den Thread zur Ausführung, sobald ein Prozessor verfügbar ist. Das heißt, kein anderer Thread mit einer höheren Priorität befindet sich derzeit im Status Bereit, und es gibt keine Kernelmodusroutinen, die mit einem höheren IRQL ausgeführt werden können.

Treiberroutinen, die unter IRQL >= DISPATCH_LEVEL ausgeführt werden, können Timeoutanforderungen mithilfe eines Timerobjekts mit einem zugeordneten DPC-Objekt in die Warteschlange einer vom Treiber bereitgestellten CustomTimerDpc-Routine ausführen. Nur Treiberroutinen, die in einem nichtarbiträren Threadkontext ausgeführt werden, können auf ein Zeitgeberobjekt auf ein Intervall ungleich null warten, wie in der vorherigen Abbildung dargestellt.

Wie jeder andere Thread wird ein vom Treiber erstellter Thread durch ein Kernelthreadobjekt dargestellt, bei dem es sich ebenfalls um ein Dispatcherobjekt handelt. Daher muss ein Treiber sein vom Treiber erstelltes Thread kein Zeitgeberobjekt verwenden, um sich freiwillig für ein bestimmtes Intervall in einen Wartezustand zu versetzen. Stattdessen kann der Thread KeDelayExecutionThread mit einem vom Aufrufer bereitgestellten Intervall aufrufen. Weitere Informationen zu diesem Verfahren finden Sie unter Abrufen eines Geräts.

DriverEntry-, Reinitialize- und Unload-Routinen werden auch in einem Systemthreadkontext ausgeführt, sodass Treiber KeWaitForSingleObject mit einem vom Treiber initialisierten Timerobjekt oder KeDelayExecutionThread aufrufen können, während sie initialisieren oder entladen. Ein Gerätetreiber kann KeStallExecutionProcessor für ein sehr kurzes Intervall (vorzugsweise weniger als 50 Mikrosekunden) aufrufen, wenn er warten muss, bis der Gerätezustand während der Initialisierung aktualisiert wird.

Treiber auf höherer Ebene verwenden jedoch im Allgemeinen einen anderen Synchronisierungsmechanismus in ihren DriverEntry - und Reinitialize-Routinen , anstatt ein Timerobjekt zu verwenden. Treiber auf höherer Ebene sollten immer so entworfen werden, dass sie sich über alle Treiber auf niedrigerer Ebene eines bestimmten Gerätetyps oder eines bestimmten Gerätetyps überlappen. Daher neigt ein Treiber höherer Ebene dazu, langsam zu laden, wenn er auf ein Timerobjekt wartet oder KeDelayExecutionThread aufruft , da ein solcher Treiber für ein Intervall lange genug warten muss, um das langsamste mögliche Gerät zu unterstützen, das ihn unterstützt. Beachten Sie auch, dass ein "sicheres", aber minimales Intervall für eine solche Wartezeit sehr schwer zu bestimmen ist.

Ebenso sollten PnP-Treiber nicht auf andere Aktionen warten, sondern stattdessen den Benachrichtigungsmechanismus des PnP-Managers verwenden.