IRQL-Anmerkungen für Treiber
Alle Treiberentwickler müssen Interrupt Request Levels (IRQLs) in Betracht ziehen. Ein IRQL ist eine ganze Zahl zwischen 0 und 31. PASSIVE_LEVEL, DISPATCH_LEVEL und APC_LEVEL werden normalerweise symbolisch und die anderen durch ihre numerischen Werte bezeichnet. Das Erhöhen und Senken des IRQL sollte einer strikten Stapeldisziplin folgen. Eine Funktion sollte darauf abzielen, dieselbe IRQL zurückzugeben, an der sie aufgerufen wurde. Die IRQL-Werte dürfen im Stapel nicht abnehmen. Und eine Funktion kann die IRQL nicht senken, ohne sie zuerst anzuheben. IRQL-Anmerkungen sollen dazu beitragen, diese Regeln zu erzwingen.
Wenn der Treibercode IRQL-Anmerkungen enthält, können die Codeanalysetools bessere Rückschlüsse auf den Bereich der Ebenen treffen, auf denen eine Funktion ausgeführt werden soll, und Fehler genauer finden. Sie können z. B. Anmerkungen hinzufügen, die die maximale IRQL angeben, mit der eine Funktion aufgerufen werden kann. Wenn eine Funktion bei einem höheren IRQL aufgerufen wird, können die Codeanalysetools die Inkonsistenzen identifizieren.
Treiberfunktionen sollten mit so vielen Informationen über den IRQL versehen werden, die möglicherweise geeignet sind. Wenn die zusätzlichen Informationen verfügbar sind, hilft sie den Codeanalysetools bei der nachfolgenden Überprüfung sowohl der aufrufenden Funktion als auch der aufgerufenen Funktion. In einigen Fällen ist das Hinzufügen einer Anmerkung eine gute Möglichkeit, ein falsch Positives zu unterdrücken. Einige Funktionen, z. B. eine Hilfsfunktion, können in jedem IRQL aufgerufen werden. In diesem Fall ist keine IRQL-Anmerkung die richtige Anmerkung.
Beim Kommentieren einer Funktion für IRQL ist es besonders wichtig, zu berücksichtigen, wie sich die Funktion entwickeln könnte, nicht nur ihre aktuelle Implementierung. Beispielsweise kann eine implementierte Funktion bei einer höheren IRQL ordnungsgemäß funktionieren als vom Designer beabsichtigt. Obwohl es verlockend ist, die Funktion basierend auf dem, was der Code tatsächlich ausführt, zu kommentieren, ist sich der Designer möglicherweise der zukünftigen Anforderungen bewusst, z. B. die Notwendigkeit, die maximale IRQL für eine zukünftige Erweiterung oder ausstehende Systemanforderungen zu senken. Die Anmerkung sollte von der Absicht des Funktions-Designers abgeleitet werden, nicht von der tatsächlichen Implementierung.
Sie können die Anmerkungen in der folgenden Tabelle verwenden, um die richtige IRQL für eine Funktion und deren Parameter anzugeben. Die IRQL-Werte werden in Wdm.h definiert.
IRQL-Anmerkung | BESCHREIBUNG |
---|---|
_IRQL_requires_max_(irql) | Die irql ist die maximale IRQL, mit der die Funktion aufgerufen werden kann. |
_IRQL_requires_min_(irql) | Irql ist die minimale IRQL, mit der die Funktion aufgerufen werden kann. |
_IRQL_requires_(irql) | Die Funktion muss an der von irql angegebenen IRQL-Instanz eingegeben werden. |
_IRQL_raises_(irql) | Die Funktion wird am angegebenen irql beendet, aber sie kann nur aufgerufen werden, um die aktuelle IRQL auszulösen (nicht zu senken). |
_IRQL_saves_ | Der mit Anmerkungen versehene Parameter speichert die aktuelle IRQL zur späteren Wiederherstellung. |
_IRQL_restores_ | Der kommentierte Parameter enthält einen IRQL-Wert aus IRQL_saves , der wiederhergestellt werden soll, wenn die Funktion zurückgibt. |
_IRQL_saves_global_(kind, param) | Der aktuelle IRQL wird an einem Speicherort gespeichert, der für die Codeanalysetools intern ist, von denen aus der IRQL wiederhergestellt werden soll. Diese Anmerkung wird verwendet, um eine Funktion mit Anmerkungen zu versehen. Der Standort wird nach Art identifiziert und durch param weiter verfeinert. Beispielsweise könnte OldIrql die Art sein, und FastMutex könnte der Parameter sein, der diesen alten IRQL-Wert enthält. |
_IRQL_restores_global_(kind, param) | Der IRQL, der von der Funktion mit Anmerkungen IRQL_saves_global gespeichert wird, wird von einem Speicherort aus wiederhergestellt, der für die Codeanalysetools intern ist. |
_IRQL_always_function_min_(Wert) | Der IRQL-Wert ist der Mindestwert, auf den die Funktion den IRQL herabsetzen kann. |
_IRQL_always_function_max_(Wert) | Der IRQL-Wert ist der maximale Wert, auf den die Funktion den IRQL auslösen kann. |
_IRQL_requires_same_ | Die kommentierte Funktion muss im selben IRQL ein- und beendet werden. Die Funktion kann den IRQL ändern, muss aber den IRQL auf seinen ursprünglichen Wert wiederherstellen, bevor er beendet wird. |
_IRQL_uses_cancel_ | Der mit Anmerkungen versehene Parameter ist der IRQL-Wert, der von einer DRIVER_CANCEL Rückruffunktion wiederhergestellt werden soll. Verwenden Sie in den meisten Fällen stattdessen die Anmerkung IRQL_is_cancel. |
Anmerkungen für DRIVER_CANCEL
Es gibt einen Unterschied zwischen den _IRQL_uses_cancel_ und _IRQL_is_cancel_ Anmerkungen. Die _IRQL_uses_cancel_ Anmerkung gibt einfach an, dass der kommentierte Parameter der IRQL-Wert ist, der von einer DRIVER_CANCEL Rückruffunktion wiederhergestellt werden soll. Die _IRQL_is_cancel_-Anmerkung ist eine zusammengesetzte Anmerkung, die aus _IRQL_uses_cancel_ und mehreren anderen Anmerkungen besteht, die das korrekte Verhalten einer DRIVER_CANCEL Rückruffunktion sicherstellen. An sich ist die _IRQL_uses_cancel_ Anmerkung nur gelegentlich nützlich; beispielsweise, wenn die übrigen von _IRQL_is_cancel_ beschriebenen Verpflichtungen auf andere Weise bereits erfüllt sind.
IRQL-Anmerkung | BESCHREIBUNG |
---|---|
_IRQL_is_cancel_ | Der kommentierte Parameter ist der IRQL, der im Rahmen des Aufrufs einer DRIVER_CANCEL Rückruffunktion übergeben wird. Diese Anmerkung gibt an, dass die Funktion ein Hilfsprogramm ist, das von Cancel-Routinen aufgerufen wird und die Anforderungen für DRIVER_CANCEL Funktionen erfüllt, einschließlich der Freigabe der Abbruch-Spin-Sperre. |
Interaktion von IRQL-Anmerkungen
IRQL-Parameteranmerkungen interagieren mehr miteinander als andere Anmerkungen, da der IRQL-Wert von den verschiedenen aufgerufenen Funktionen festgelegt, zurückgesetzt, gespeichert und wiederhergestellt wird.
Angeben der maximalen und minimalen IRQL
Die anmerkungen _IRQL_requires_max_ und _IRQL_requires_min_ geben an, dass die Funktion nicht von einer IRQL aufgerufen werden soll, die höher oder niedriger als der angegebene Wert ist. Wenn PREfast beispielsweise eine Sequenz von Funktionsaufrufen sieht, die den IRQL nicht ändern, und wenn es einen mit einem _IRQL_requires_max_ Wert findet, der sich unterhalb eines in der Nähe befindlichen _IRQL_requires_min_ befindet, wird eine Warnung für den zweiten Aufruf gemeldet, auf den er stößt. Der Fehler kann tatsächlich beim ersten Aufruf auftreten. Die Meldung gibt an, wo die andere Hälfte des Konflikts aufgetreten ist.
Wenn die Anmerkungen für eine Funktion den IRQL Erwähnung und nicht explizit _IRQL_requires_max_ anwenden, wendet das Codeanalysetool implizit die Anmerkung _IRQL_requires_max_(DISPATCH_LEVEL) an. Dies ist in der Regel bei seltenen Ausnahmen richtig. Durch die implizite Anwendung als Standard werden viele Anmerkungswirrigkeiten vermieden, und die Ausnahmen werden deutlich sichtbarer.
Die _IRQL_requires_min_(PASSIVE_LEVEL)-Anmerkung wird immer impliziert, da die IRQL nicht niedriger gehen kann. Daher gibt es keine entsprechende explizite Regel über die Mindest-IRQL. Nur sehr wenige Funktionen verfügen sowohl über eine andere Obergrenze als DISPATCH_LEVEL als auch über eine untere Grenze außer PASSIVE_LEVEL.
Einige Funktionen werden in einem Kontext aufgerufen, in dem die aufgerufene Funktion den IRQL nicht sicher über einen bestimmten Höchstwert anheben kann oder ihn häufiger nicht sicher unter ein Minimum senken kann. Die _IRQL_always_function_max_ und _IRQL_always_function_min_ Anmerkungen helfen PREfast dabei, Fälle zu finden, in denen dies unbeabsichtigt auftritt.
Beispielsweise werden Funktionen vom Typ DRIVER_STARTIO mit _IRQL_always_function_min_(DISPATCH_LEVEL) kommentiert. Dies bedeutet, dass es während der Ausführung einer DRIVER_STARTIO-Funktion ein Fehler ist, den IRQL unter DISPATCH_LEVEL zu senken. Andere Anmerkungen weisen darauf hin, dass die Funktion bei DISPATCH_LEVEL eingegeben und beendet werden muss.
Angeben einer expliziten IRQL
Verwenden Sie die _IRQL_raises_- oder _IRQL_requires_-Anmerkung, um PREfast dabei zu helfen, eine Inkonsistenz zu melden, die mit _IRQL_requires_max_ oder _IRQL_requires_min_ Anmerkungen erkannt wird, da es dann den IRQL kennt.
Die _IRQL_raises_-Anmerkung gibt an, dass eine Funktion zurückgibt, wobei der IRQL auf einen neuen Wert festgelegt ist. Wenn Sie die _IRQL_raises_-Anmerkung verwenden, wird auch die _drv_maxFunctionIRQL Anmerkung effektiv auf den gleichen IRQL-Wert festgelegt. Wenn die Funktion den IRQL jedoch höher als den endgültigen Wert anhebt und ihn dann auf den endgültigen Wert senkt, sollten Sie eine explizite _IRQL_always_function_max_ Anmerkung nach der _IRQL_raises_ Anmerkung hinzufügen, um den höheren IRQL-Wert zu ermöglichen.
Auslösen oder Herabsetzen von IRQL
Die _IRQL_raises_-Anmerkung gibt an, dass die Funktion nur zum Auslösen von IRQL und nicht zum Senken von IRQL verwendet werden darf, auch wenn die Syntax der Funktion dies zulässt. KeRaiseIrql ist ein Beispiel für eine Funktion, die nicht verwendet werden sollte, um IRQL zu senken.
Speichern und Wiederherstellen von IRQL
Verwenden Sie die _IRQL_saves_ und _IRQL_restores_ Anmerkungen, um anzugeben, dass der aktuelle IRQL (unabhängig davon, ob er genau oder nur ungefähr bekannt ist) im kommentierten Parameter gespeichert oder aus diesem wiederhergestellt wird.
Einige Funktionen speichern und stellen den IRQL implizit wieder her. Beispielsweise speichert die ExAcquireFastMutex-Systemfunktion den IRQL an einer undurchsichtigen Position, die dem schnellen Mutex-Objekt zugeordnet ist, das der erste Parameter identifiziert. der gespeicherte IRQL wird von der entsprechenden ExReleaseFastMutex-Funktion für dieses schnelle Mutex-Objekt wiederhergestellt. Um diese Aktionen explizit anzugeben, verwenden Sie die anmerkungen _IRQL_saves_global_ und _IRQL_restores_global_. Die Parameter kind und param geben an, wo der IRQL-Wert gespeichert wird. Der Speicherort, an dem der Wert gespeichert wird, muss nicht genau angegeben werden, solange die Anmerkungen, die den Wert speichern und wiederherstellen, konsistent sind.
Beibehalten desselben IRQL
Sie sollten alle Vom Treiber erstellten Funktionen kommentieren, die die IRQL ändern, indem Sie entweder die _IRQL_requires_same_-Anmerkung oder eine der anderen IRQL-Anmerkungen verwenden, um anzugeben, dass die Änderung in IRQL erwartet wird. Wenn keine Anmerkungen vorhanden sind, die auf eine Änderung in IRQL hinweisen, geben die Codeanalysetools eine Warnung für jede Funktion aus, die nicht bei derselben IRQL beendet wird, an der die Funktion eingegeben wurde. Wenn die Änderung in IRQL beabsichtigt ist, fügen Sie die entsprechende Anmerkung hinzu, um den Fehler zu unterdrücken. Wenn die Änderung in IRQL nicht beabsichtigt ist, sollte der Code korrigiert werden.
Speichern und Wiederherstellen von IRQL für E/A-Abbruchroutinen
Verwenden Sie die _IRQL_uses_cancel_ Anmerkung, um anzugeben, dass der mit Anmerkungen versehene Parameter der IRQL-Wert ist, der von einer DRIVER_CANCEL Rückruffunktion wiederhergestellt werden soll. Diese Anmerkung gibt an, dass die Funktion ein Hilfsprogramm ist, das von Abbruchroutinen aufgerufen wird, und dass sie die Anforderungen erfüllt, die für DRIVER_CANCEL Funktionen gestellt wurden (das heißt, sie entlädt die Verpflichtung für den Aufrufer).
Im Folgenden wird beispielsweise die Deklaration für den DRIVER_CANCEL Rückruffunktionstyp angezeigt. Einer der Parameter ist der IRQL, der von dieser Funktion wiederhergestellt werden soll. Die Anmerkungen geben alle Anforderungen einer Abbruchfunktion an.
// Define driver cancel routine type. //
__drv_functionClass(DRIVER_CANCEL)
_Requires_lock_held_(_Global_cancel_spin_lock_)
_Releases_lock_(_Global_cancel_spin_lock_)
_IRQL_requires_min_(DISPATCH_LEVEL)
_IRQL_requires_(DISPATCH_LEVEL)
typedef
VOID
DRIVER_CANCEL (
_Inout_ struct _DEVICE_OBJECT *DeviceObject,
_Inout_ _IRQL_uses_cancel_ struct _IRP *Irp
);
typedef DRIVER_CANCEL *PDRIVER_CANCEL;