IRQL_NOT_LESS_OR_EQUAL 到底是什么?什么是 IRQL?什么东西使用 IRQL?为什么它需要小于或等于?什么会导致它不小于或等于?为什么操作系统无法从不小于或等于中恢复?IRQL 只影响 Windows 吗?
这个错误似乎是很常见。我不是在寻求帮助,我是在寻求一个解释。
答案1
情况很复杂。 ;)
不,确实如此。
IRQL 代表“中断请求级别”。它是一个数字,在 Windows x86 系统上范围为 0 到 31,在 x64 系统上范围为 0 到 15。它表示内核模式任务相对于其他内核模式任务的“重要性”。
IRQL 是 Windows 定义的处理器状态(不是进程或线程的状态),它向 Windows 指示该处理器正在执行的操作是否可以被其他任务中断。如果新任务(例如中断服务例程)的 IRQL 高于处理器的当前 IRQL,则它可以中断当前任务;否则不行。在多处理器系统上,每个处理器都有自己的 IRQL。这包括由超线程创建的“逻辑处理器”。
(我使用“重要性”而不是“优先级”这个词,因为 Windows 中的“优先级”是指线程优先级,而 IRQL 是不同的东西。与线程优先级不同,同一 IRQL 上的内核任务没有时间分片,并且 IRQL 不会自动提升和衰减。)
(我还应该提到,这里的“内核任务”一词不是官方的。Windows 并没有真正把这些东西称为“内核任务”,它们不是托管对象,例如进程和线程,并且与 x86“任务门”或“任务管理器”中显示的任何内容都没有关系。正如我(和其他人)在这里使用的术语,“内核模式任务”实际上涵盖了“任何有定义的开始和结束的事情,需要在 IRQL 2 或更高级别的内核模式下完成。”中断服务例程是“内核模式任务”的一个例子;DPC 例程也是。但另一个例子可以是内核模式线程中的代码。这样的线程从 IRQL 0 开始,但如果代码的一部分加薪到 IRQL 2 或更高,执行某些操作,然后返回到其先前的 IRQL,代码的高 IRQL 部分就是我在此称之为“内核任务”的一个例子。)
性能监视器将 IRQL 2 上所花费的时间显示为“% DPC 时间”,将 IRQL > 2 上所花费的时间显示为“% 中断时间”,无论该时间实际上是花在 DPC 例程或 ISR 上,还是将 IRQL 从较低值提升的结果。每个都是 PerfMon 显示的“% 特权时间”的子集 - 应该标记为“内核模式时间”。
一旦内核任务以 IRQL 2 或更高级别启动,它就会先于其他任务运行完成。相同的IRQL 将在同一处理器上启动。它可能被更高 IRQL 的任务中断(而更高 IRQL 的任务又可能被更高 IRQL 的任务中断,等等),但当更高 IRQL 的任务完成时,控制权将返回到它中断的任务。
IRQL 主要是一个序列化机制。(许多人说“同步”,但我更喜欢这个词,因为它更准确地描述了结果。)其目的是帮助保证同一 CPU 上的访问某些共享资源(主要是 OS 内核空间中的共享数据结构)的多个任务不允许以可能破坏这些结构的方式互相中断。
例如,Windows 内核中的大量数据,特别是内存管理数据和线程调度程序使用的数据,“序列化”IRQL 为 2。这意味着任何想要修改此类数据的任务都必须在 IRQL 2 下运行。如果更高 IRQL 的任务尝试写入此类数据,则可能会导致损坏,因为它可能中断了可能正在对同一数据进行读取-修改-写入循环的 IRQL 2 任务。因此,更高 IRQL 的任务根本不允许这样做。
IRQL 较高的任务主要是设备驱动程序的中断服务例程,因为所有设备的中断都发生在 IRQL > 2 上。这包括主板上的定时器芯片的中断,该芯片驱动操作系统中的计时和时间驱动活动。它的 IRQL 高于所有“普通”硬件设备的 IRQL。
IRQL 2 及以上用于不由硬件中断触发的内核任务,但在此期间无法进行正常的线程调度(包括等待)。因此,一旦处理器处于 IRQL 2 或以上,则该处理器上不会发生任何线程上下文切换,直到 IRQL 降至 2 以下。
用户模式代码始终处于 IRQL 0。内核模式代码可以在从 0 到最大值的任何 IRQL 下运行。IRQL 1 是一种特殊情况;它仅是内核模式,但对调度没有影响,并且实际上更像是线程的状态而不是处理器的状态 - 例如,它在线程上下文切换期间被保存和恢复。
为了维护各种序列化保证,大多数异常(例如除以零或内存访问冲突,例如页面错误)根本无法在 IRQL 2 或更高级别处理。(顺便说一下,IRQL 2 通常称为“调度级别”或“DPC 级别”。)
现在我们终于可以解释这个错误检查代码了!
IRQL_NOT_LESS_OR_EQUAL 的最常见情况是由于页面错误(尝试访问“非驻留”虚拟地址)或内存访问冲突(尝试写入只读页面或访问根本没有定义的页面),发生在 IRQL 2 或更高版本。
如果此类异常在 IRQL 0 或 1 上引发,则它们可以由系统提供的代码(如页面错误处理程序)或开发人员提供的异常处理程序“处理”。但是,如果它们发生在 IRQL 2 或更高级别,则大多数异常根本无法处理。
因此...错误检测代码的意思是“当 IRQL 为 2 或更高时,发生了只能在 IRQL 0 或 1 上处理的类型的异常。”即“不小于或等于 1”。措辞很奇怪,但事实就是如此。
还有一些其他因素可以触发此错误检查,并且 IRQL 不小于或等于的值并不总是 1,但它们很少发生。WinDBG 文档列出了它们。