Was genau ist IRQL_NOT_LESS_OR_EQUAL? Was ist IRQL? Welche Dinge verwenden IRQL? Warum muss es kleiner oder gleich sein? Was würde dazu führen, dass es nicht kleiner oder gleich ist? Warum kann das Betriebssystem nicht wiederhergestellt werden, wenn es nicht kleiner oder gleich ist? Betrifft IRQL nur Windows?
Dieser Fehler scheintziemlich häufigIch bitte nicht um Hilfe, ich bitte um eine Erklärung.
Antwort1
Es ist kompliziert. ;)
Nein, im Ernst, das ist es.
IRQL steht für „Interrupt Request Level“. Es handelt sich um eine Zahl im Bereich von 0 bis 31 auf Windows x86-Systemen und 0 bis 15 auf x64-Systemen. Sie stellt die „Wichtigkeit“ einer Kernelmodusaufgabe im Verhältnis zu anderen Kernelmodusaufgaben dar.
IRQL ist ein von Windows definierter Zustand des Prozessors – nicht eines Prozesses oder Threads – der Windows anzeigt, ob die Arbeit des Prozessors durch andere Tasks unterbrochen werden kann oder nicht. Wenn eine neue Task (z. B. eine Interrupt-Serviceroutine) einen höheren IRQL als den aktuellen IRQL des Prozessors hat, dann kann sie die aktuelle Task unterbrechen, andernfalls nicht. Auf einem Mehrprozessorsystem hat jeder Prozessor seinen eigenen IRQL. Dazu gehören auch die „logischen Prozessoren“, die durch Hyperthreading erstellt werden.
(Ich verwende das Wort „Wichtigkeit“ statt „Priorität“, weil sich „Priorität“ in Windows auf Thread-Prioritäten bezieht und IRQLs etwas anderes sind. Im Gegensatz zu Thread-Prioritäten werden Kernel-Tasks mit demselben IRQL nicht in Zeitscheiben aufgeteilt und IRQLs unterliegen keinem automatischen Boost und Decay.)
( Ich sollte auch erwähnen, dass der Begriff „Kernel-Task“ hier nicht offiziell ist. Windows nennt diese Dinge nicht wirklich „Kernel-Tasks“, sie sind keine verwalteten Objekte wie z. B. Prozesse und Threads, und es besteht keine Beziehung zu x86-„Task-Gates“ oder zu irgendetwas, das im „Task-Manager“ angezeigt wird. So wie ich (und andere) den Begriff hier verwenden, umfasst „Kernel-Modus-Task“ wirklich „alles mit einem definierten Anfang und Ende, das im Kernel-Modus bei IRQL 2 oder höher ausgeführt werden muss.“ Eine Interrupt-Service-Routine ist ein Beispiel für einen „Kernel-Modus-Task“, ebenso wie eine DPC-Routine. Ein anderes Beispiel kann jedoch Code in einem Kernel-Modus-Thread sein. Solche Threads beginnen bei IRQL 0, aber wenn ein Teil des Codeserhöhtauf IRQL 2 oder höher, führt etwas aus und kehrt dann zu seinem vorherigen IRQL zurück. Der High-IRQL-Teil des Codes ist ein Beispiel für das, was ich hier eine „Kernel-Aufgabe“ nenne.)
Der Leistungsmonitor zeigt die bei IRQL 2 verbrachte Zeit als „% DPC-Zeit“ und die bei IRQL > 2 verbrachte Zeit als „% Unterbrechungszeit“, unabhängig davon, ob die Zeit tatsächlich in einer DPC-Routine oder ISR verbracht wurde oder das Ergebnis einer Erhöhung von IRQL von einem niedrigeren Wert war. Jeder Wert ist eine Teilmenge dessen, was PerfMon als „% privilegierte Zeit“ anzeigt – was eigentlich als „Kernelmoduszeit“ hätte bezeichnet werden sollen.
Sobald eine Kernel-Aufgabe mit IRQL 2 oder höher gestartet wird, wird sie vor allem anderen auf derDasselbeIRQL wird auf demselben Prozessor gestartet. Es kann durch eine Aufgabe mit höherem IRQL unterbrochen werden (die wiederum durch eine Aufgabe mit noch höherem IRQL unterbrochen werden könnte usw.), aber wenn die Aufgaben mit höherem IRQL abgeschlossen sind, geht die Steuerung an die Aufgabe zurück, die sie unterbrochen hat.
IRQL ist in erster Linie einSerialisierungMechanismus. (Viele sagen „Synchronisierung“, aber ich bevorzuge dieses Wort, da es das Ergebnis genauer beschreibt.) Sein Zweck besteht darin, zu gewährleisten, dass mehrere Aufgaben auf derselben CPU, die auf bestimmte gemeinsam genutzte Ressourcen zugreifen – meist gemeinsam genutzte Datenstrukturen im Kernelbereich des Betriebssystems – sich nicht gegenseitig auf eine Weise unterbrechen dürfen, die diese Strukturen beschädigen könnte.
Beispielsweise werden viele Daten im Windows-Kernel, insbesondere die Speicherverwaltungsdaten und die vom Thread-Scheduler verwendeten Daten,"serialisiert"bei IRQL 2. Das bedeutet, dass jede Aufgabe, die solche Daten ändern möchte, dabei bei IRQL 2 ausgeführt werden muss. Wenn eine Aufgabe mit höherem IRQL versucht, solche Daten zu schreiben, kann dies zu Beschädigungen führen, da sie möglicherweise eine IRQL 2-Aufgabe unterbrochen hat, die sich möglicherweise mitten in einem Lese-Änderungs-Schreibzyklus für dieselben Daten befindet. Aufgaben mit höherem IRQL dürfen dies also einfach nicht tun.
Aufgaben mit höherem IRQL sind meist die Interrupt-Serviceroutinen von Gerätetreibern, da alle Geräteinterrupts bei IRQL > 2 auftreten. Dazu gehört auch der Interrupt vom Timer-Chip auf der Hauptplatine, der die Zeitmessung und zeitgesteuerte Aktivitäten im Betriebssystem steuert. Sein IRQL liegt über dem aller „normalen“ Hardwaregeräte.
IRQLs 2 und höher werden für Kernelaufgaben verwendet, die nicht durch Hardware-Interrupts ausgelöst werden, bei denen aber keine normale Threadplanung – einschließlich Warten – stattfinden kann. Sobald ein Prozessor IRQL 2 oder höher erreicht hat, können auf diesem Prozessor keine Threadkontextwechsel mehr stattfinden, bis IRQL unter 2 fällt.
Code im Benutzermodus befindet sich immer auf IRQL 0. Code im Kernelmodus kann auf jedem IRQL von 0 bis zum Maximum ausgeführt werden. IRQL 1 ist ein Sonderfall; es handelt sich nur um den Kernelmodus, hat aber keine Auswirkungen auf die Planung und ist eigentlich eher ein Zustand eines Threads als des Prozessors – er wird beispielsweise während Threadkontextwechseln gespeichert und wiederhergestellt.
Um verschiedene Serialisierungsgarantien einzuhalten, können die meisten Ausnahmen (Dinge wie Division durch Null oder Speicherzugriffsverletzungen wie Seitenfehler) bei IRQL 2 oder höher einfach nicht behandelt werden. (IRQL 2 wird übrigens häufig als „Dispatch-Level“ oder „DPC-Level“ bezeichnet.)
Und jetzt können wir diesen Bugcheck-Code endlich erklären!
Der häufigste Fall von IRQL_NOT_LESS_OR_EQUAL ist auf einen Seitenfehler (Versuch, auf eine „nicht residente“ virtuelle Adresse zuzugreifen) oder eine Speicherzugriffsverletzung (Versuch, auf eine schreibgeschützte Seite zu schreiben oder auf eine Seite zuzugreifen, die überhaupt nicht definiert ist) zurückzuführen, der bei IRQL 2 oder höher auftritt.
Wenn solche Ausnahmen bei IRQL 0 oder 1 auftreten, können sie entweder durch vom System bereitgestellten Code (wie den Seitenfehlerhandler) oder durch einen vom Entwickler bereitgestellten Ausnahmehandler „behandelt“ werden. Die meisten Ausnahmen können jedoch überhaupt nicht behandelt werden, wenn sie bei IRQL 2 oder höher auftreten.
Der Bugcheck-Code bedeutet also: „Eine Ausnahme eines Typs, der nur bei IRQL 0 oder 1 behandelt werden kann, ist aufgetreten, als IRQL 2 oder höher war.“ also „nicht kleiner oder gleich 1“. Seltsame Formulierung, aber so ist es nun einmal.
Es gibt noch ein paar andere Dinge, die diesen Bugcheck auslösen können, und der Wert, bei dem der IRQL nicht kleiner oder gleich ist, ist nicht immer 1, aber sie kommen nur selten vor. Die WinDBG-Dokumentation listet sie auf.