Freigeben über


Verwenden einer IoTimer-Routine

Während der Timer für das zugeordnete Geräteobjekt aktiviert ist, wird die IoTimer-Routine ungefähr einmal pro Sekunde aufgerufen. Da die Intervalle, in denen jede IoTimer-Routine aufgerufen wird, jedoch von der Auflösung der Systemuhr abhängen, gehen Sie nicht davon aus, dass eine IoTimer-Routine genau auf einer Begrenzung von einer Sekunde aufgerufen wird.

Hinweis Eine IoTimer-Routine wird wie alle DPC-Routinen unter IRQL = DISPATCH_LEVEL aufgerufen. Während eine DPC-Routine ausgeführt wird, werden alle Threads daran gehindert, auf demselben Prozessor ausgeführt zu werden. Treiberentwickler sollten ihre IoTimer-Routinen so sorgfältig entwerfen, dass sie so kurz wie möglich ausgeführt werden.

Die wahrscheinlich häufigste Verwendung für eine IoTimer-Routine ist das Timeout von Geräte-E/A-Vorgängen für eine IRP. Betrachten Sie das folgende Szenario für die Verwendung einer IoTimer-Routine als ausgeführten Timer in einem Gerätetreiber:

  1. Beim Starten des Geräts initialisiert der Treiber einen Timerzähler in der Geräteerweiterung auf -1, der keine aktuellen Geräte-E/A-Vorgänge angibt, und ruft IoStartTimer auf, bevor er STATUS_SUCCESS zurückgibt.

    Jedes Mal, wenn die IoTimer-Routine aufgerufen wird, überprüft sie, ob der Timerzähler -1 ist, und gibt, falls ja, die Steuerung zurück.

  2. Die StartIo-Routine des Treibers initialisiert den Timerzähler in der Geräteerweiterung auf eine Obergrenze und eine zusätzliche Sekunde, falls die IoTimer-Routine gerade ausgeführt wurde. Anschließend wird KeSynchronizeExecution verwendet, um eine SynchCritSection_1-Routine aufzurufen, die das physische Gerät für den vom aktuellen IRP angeforderten Vorgang programmiert.

  3. Der ISR des Treibers setzt den Timerzähler auf -1 zurück, bevor die DpcForIsr-Routine des Treibers oder eine CustomDpc-Routine anstehen.

  4. Jedes Mal, wenn die IoTimer-Routine aufgerufen wird, überprüft sie, ob der Timerzähler vom ISR auf -1 zurückgesetzt wurde, und gibt, falls ja, die Steuerung zurück. Andernfalls ruft die IoTimer-Routinemithilfe von KeSynchronizeExecution eine SynchCritSection_2 Routine auf, die den Timerzähler um eine vom Treiber bestimmte Anzahl von Sekunden anpasst.

  5. Die SynchCritSection_2 Routine gibt TRUE an die IoTimer-Routine zurück, solange für die aktuelle Anforderung noch kein Timeout aufgetreten ist. Wenn der Timerzähler auf 0 (Null) wechselt, setzt die SynchCritSection_2 Routine den Timerzähler auf einen vom Treiber ermittelten Wert für reset-timeout zurück, legt ein erwartetes Zurücksetzungsflag für sich selbst (und für den DpcForIsr) in seinem Kontextbereich fest, versucht, das Gerät zurückzusetzen, und gibt TRUE zurück.

    Die SynchCritSection_2 Routine wird erneut aufgerufen, wenn der Zurücksetzungsvorgang auch auf dem Gerät ein Zeitüberschreitungsvorgang aufweist, wenn FALSE zurückgegeben wird. Wenn das Zurücksetzen erfolgreich ist, ermittelt die DpcForIsr-Routine , dass das Gerät vom erwarteten Zurücksetzungsflag zurückgesetzt wurde, und wiederholt die Anforderung, wobei die Aktionen der StartIo-Routine wie in Schritt 2 beschrieben wiederholt werden.

  6. Wenn die SynchCritSection_2 Routine FALSE zurückgibt, geht die IoTimer-Routine davon aus, dass sich das physische Gerät in einem unbekannten Zustand befindet, da der Versuch, es zurückzusetzen, bereits fehlgeschlagen ist. Unter diesen Umständen stellt die IoTimer-Routine eine CustomDpc-Routine in die Warteschlange und gibt zurück. Diese CustomDpc-Routine protokolliert einen Geräte-E/A-Fehler, ruft IoStartNextPacket auf, schlägt den aktuellen IRP aus und gibt zurück.

Wenn der ISR dieses Gerätetreibers den freigegebenen Timerzähler auf -1 zurücksetzt, wie in Schritt 3 beschrieben, schließt die DpcForIsr-Routine des Treibers die interruptgesteuerte E/A-Verarbeitung des aktuellen IRP ab. Der Timerzähler zum Zurücksetzen gibt an, dass bei diesem Geräte-E/A-Vorgang kein Timeout aufgetreten ist, sodass die IoTimer-Routine den Timerzähler nicht ändern muss.

In den meisten Fällen dekrementiert die vorherige SynchCritSection_2 Routine einfach den Timerzähler. Die SynchCritSection_2 Routine versucht, das Gerät nur zurückzusetzen, wenn für den aktuellen E/A-Vorgang ein Timeout aufgetreten ist. Dies wird angezeigt, wenn der Timerzähler auf 0 0 steigt. Und nur wenn der Versuch, das Gerät zurückzusetzen, bereits fehlgeschlagen ist, gibt die SynchCritSection_2 Routine FALSE an die IoTimer-Routine zurück.

Daher benötigen sowohl die vorherige IoTimer-Routine als auch deren Helfer SynchCritSection_2 Routine sehr wenig Zeit, um unter normalen Umständen auszuführen. Durch die Verwendung einer IoTimer-Routine auf diese Weise stellt ein Gerätetreiber sicher, dass jede gültige Geräte-E/A-Anforderung bei Bedarf wiederholt werden kann, und dass eine CustomDpc-Routine einen IRP nur dann fehlschlägt, wenn ein nicht korrigierbarer Hardwarefehler verhindert, dass die IRP erfüllt wird. Darüber hinaus stellt der Treiber diese Funktionalität zu sehr geringen Kosten in der Ausführungszeit bereit.

Die Einfachheit des vorherigen Szenarios hängt von einem Gerät ab, das jeweils nur einen Vorgang ausführt, und von einem Treiber, der sich normalerweise nicht über E/A-Vorgänge überschneidet. Ein Treiber, der überlappende Geräte-E/A-Vorgänge ausführt, oder ein Treiber auf höherer Ebene, der eine IoTimer-Routine verwendet, um eine Reihe von treiberseitig zugewiesenen IRPs zu timen, die an mehr als eine Kette niedrigerer Treiber gesendet werden, hätte komplexere Timeoutszenarien zu verwalten.