如果您曾经看到过蓝屏,并显示类似以下消息 IRQL_NOT_LESS_OR_EQUAL o DRIVER_IRQL_NOT_LESS_OR_EQUAL你可能遇到过一个在驱动程序领域之外鲜为人知的概念:IRQL(中断请求级别)。在 Windows,当系统超过某个阈值时,该级别的中断优先级优先于线程优先级,这对稳定性有直接影响。
在接下来的几行中你会发现 一 完整指南 以及西班牙的西班牙语关于 IRQL 是什么,它是如何工作的、为什么它会触发蓝屏、如何使用 WinDbg 诊断问题,以及无论您是遇到错误的用户还是正在开发内核模式驱动程序,都应该采取哪些措施。让我们开始吧。
Windows 中的 IRQL(中断请求级别)是什么?
在 Windows 中, IRQL 定义 硬件 处理器运行 在任何给定时间。在 Windows 驱动模型 (WDM) 中,以低 IRQL 运行的代码可能会被以高 IRQL 运行的代码中断。事实上,在一台多核计算机上,每个 CPU 可能处于不同的 IRQL,这使得同步变得复杂。
有一条关键规则: 当 CPU 以高于 PASSIVE_LEVEL 的 IRQL 运行时,它只能被更高 IRQL 的活动抢占。这组织了用户代码、内核函数、延迟调用程序 (DPC) 和设备中断服务例程 (ISR) 之间的共存。
级别和优先级:PASSIVE_LEVEL、APC_LEVEL、DISPATCH_LEVEL 和 DIRQL
笼统, 在 x86 上,使用 0 到 31 之间的 IRQL 值;在 x64 上,使用 0 到 15 之间的实际意义是一样的:IRQL 0(PASSIVE_LEVEL)是执行正常用户代码和许多驱动程序函数的地方; APC 和页面错误 它们通常映射到 IRQL 1(APC_LEVEL);IRQL 2(DISPATCH_LEVEL)包含线程调度程序和 DPC。高于 DISPATCH_LEVEL 的级别是为设备中断(称为 DIRQL)和其他内部用途保留的级别,例如 HIGH_LEVEL。
在驾驶员生态系统中, 许多常见例程在 DISPATCH_LEVEL 级别运行:例如,DPC 和 StartIo。这种设计确保当其中一个例程正在接触内部队列或其他共享资源时,同一级别的另一个例程不会在该 CPU 上抢占它,因为抢占规则只允许更高级别的中断。
在 DISPATCH_LEVEL 和分析/高级别之间有空间 每个设备的硬件中断(DIRQL)设备的 IRQL 定义了其相对于其他设备的优先级。WDM 驱动程序在 IRP_MJ_PNP 期间使用 IRP_MN_START_DEVICE 获取此 IRQL。此设备 IRQL 不是一个全局的固定值,而是与特定中断线关联的值。
IRQL 与线程优先级
建议不要混淆概念: 线程优先级决定调度程序何时抢占以及哪个线程执行IRQL 控制哪些类型的活动可以执行以及哪些中断被屏蔽。在 DISPATCH_LEVEL 以上,没有线程切换:控制的是 IRQL,而不是线程优先级。
IRQL 和分页:你不应该做的事情
提高 IRQL 的一个直接影响是系统 无法处理页面错误黄金法则:在 DISPATCH_LEVEL 或更高级别运行的代码不会导致页面错误。实际上,这意味着这些例程及其接触的数据 必须驻留在非分页内存中此外,某些内核辅助程序会根据 IRQL 限制其使用:例如, KeWaitForSingleObject
仅当您没有阻塞(零超时)时才能调用 DISPATCH_LEVEL,而对于非零超时,您需要低于 DISPATCH_LEVEL。
IRQL 的隐式和显式控制
大多数时候, 系统本身以正确的 IRQL 调用你的例程 它们应该做什么。IRP 的调度例程在 PASSIVE_LEVEL 级别运行(它们可以阻止或调用任何辅助程序),StartIo 和 DPC 在 DISPATCH_LEVEL 级别运行以保护共享队列,ISR 在 DIRQL 级别运行。
如果你需要明确控制它, 您可以使用以下方式提高或降低 IRQL KeRaiseIrql
y KeLowerIrql
有一个非常常用的快捷方式: KeRaiseIrqlToDpcLevel()
返回之前的 IRQL,并让你停留在 DISPATCH_LEVEL 级别。重要提示:切勿将 IRQL 降低到低于系统调用时的值;打破同步可能会导致非常严重的竞争窗口。
与 IRQL 相关的蓝屏错误:IRQL_NOT_LESS_OR_EQUAL 和 DRIVER_IRQL_NOT_LESS_OR_EQUAL
与这些问题相关的两个经典错误检查是 IRQL_NOT_LESS_OR_EQUAL (0xA) y DRIVER_IRQL_NOT_LESS_OR_EQUAL (0xD1)两者都表示尝试以过高的 IRQL 访问可分页(或无效)地址。这通常是由于驱动程序使用了错误的地址、取消引用了错误的指针或在不适当的级别执行了可分页代码。
在具体情况下 DRIVER_IRQL_NOT_LESS_OR_EQUAL (0x000000D1),参数信息非常丰富:1)引用的内存地址;2)当时的IRQL;3)访问类型(0 读取,1 写入,2/8 执行);4)引用内存的指令地址。使用调试器,您可以使用 ln
参数 4 列出最近的符号并知道正在运行什么函数.
需要注意的常见原因
除了特定的代码之外,还有一些重复的模式。 取消引用指向 DISPATCH_LEVEL 或更高级别的无效指针 这肯定会导致灾难。访问该级别的可分页数据,或执行可分页代码(例如,标记为可分页的函数),也会触发错误检查。
其他常见情况包括 调用另一个已下载的驱动程序中的函数 (悬空函数指针),或通过无效函数指针间接调用。通常,如果系统能够识别某个模块,您会在蓝屏上看到它的名称,并且它也会保存在 KiBugCheckDriver
,可通过 dx KiBugCheckDriver
来自 WinDbg。
一个实际的细节: 在大多数 D1/A 中,真正的问题不是 IRQL 本身,而是引用的内存地址。这就是为什么参数 1、3 和 4 对于诊断至关重要。
使用 WinDbg 进行诊断:有用的命令和参数读取
为了处理这些案件, WinDbg 是关键工具,如果 BSOD 提到 NTOSKRNL.EXE 这些信息提供了许多关于故障是否发生在内核子系统方面的指导。首先 !analyze -v
获取错误检查、堆栈以及(如果幸运的话)涉及的模块的摘要。如果转储包含捕获帧, .trap
让您处于故障 CPU 的环境中。
MGI comandos 桩作为 k
, kb
, kc
, kd
, kp
, kP
, kv
它们会向您展示不同级别的回溯细节。 ln
对于参数 4,你可以跳过 指向该内存的指令 并获取附近的符号。如果你怀疑优先级在中断之前运行, !irql
显示目标处理器已保存的 IRQL(例如 DISPATCH_LEVEL)。
分析参数1的方向, !pool
它会告诉您它是否属于分页池; !address
y !pte
深入研究该区域的内存映射。你可以使用 内存显示命令 检查尝试访问的内容。最后, u
, ub
, uu
允许您围绕参数 4 的地址进行反汇编。
不要忘了 lm t n
列出已加载的模块 y !memusage
对于记忆的一般状态。如果 KiBugCheckDriver
有东西, dx KiBugCheckDriver
它将返回 Unicode 模块的名称:在典型的例子中,“Wdf01000.sys”被视为错误检查期间涉及的驱动程序。
系统工具:驱动程序验证器、事件查看器和诊断程序
El 驱动程序验证程序 实时检查驱动程序的行为,并在检测到不正确的资源使用情况(例如池)时强制执行错误,引发异常以隔离代码中的问题区域。它通过以下方式启动: verifier
从 命令提示 建议选择尽可能最小的驱动程序集,以避免增加太多开销。
如果你不熟悉 WinDbg, 采取基本措施:在事件查看器中检查系统日志,查找指向特定设备/驱动程序的错误;更新或禁用蓝屏提示的驱动程序;验证硬件与您的 Windows 版本的兼容性;如果您怀疑 RAM 有问题,请使用 Windows 内存诊断程序。这些操作虽然简单,但 他们破获了大量案件.
真实案例:BSOD 看似随机
拥有 Windows 10 Pro(AMD Ryzen 5 3400G CPU, GPU NVIDIA公司 GeForce GTX 1660 Ti 和 Gigabyte B450 AORUS PRO WIFI 主板(16 GB 内存)偶尔会出现“IRQL_LESS_OR_NOT_EQUAL”屏幕。我已经更新了必要的驱动程序(网络、显卡),安装了所有 Windows 更新,并运行了内存工具,但都没有检测到任何问题。
在这样的场景中, 下一步是使用 WinDbg 分析转储 并寻找模式:当它掉落时所涉及的过程(例如, explorer.exe
)、图形界面模块(win32kfull.sys
) 以及诸如 xxxProcessNotifyWinEvent
出现在堆栈中。虽然此模块是 Windows 的,但触发器通常是第三方驱动程序(图形、输入、覆盖、捕获卡),它以不合适的 IRQL 使用内存,并且故障发生在 win32k
.
这里的实际建议是 暂时禁用覆盖软件 (捕获、GPU OSD)、激进的软件外设驱动程序(带宏的鼠标/键盘)以及显卡驱动程序的 Beta 版本,并缩小问题范围。使用驱动程序验证程序对可疑问题进行分析,有助于缩小问题范围,并让问题堆栈更加清晰。
一种非常常见的网络模式:ndis.sys 并不总是罪魁祸首
另一个典型案例: ndis.sys 的屏幕截图 (Windows 网络层)。在实际的计算机上,系统启动后会立即崩溃。实际的解决方案是启动到 安全模式 没有网络功能时,打开 设备管理器 并禁用“网络适配器”下的适配器以隔离问题。
在那支球队里, Realtek PCIe GBE 系列控制器和 Atheros AR5007G。通过停用两者,检测到真正的原因是 athrx.sys
(Atheros),虽然蓝屏提到 ndis.sys
转储证实了这一点:堆栈通过 ndis!NdisFreeTimerObject
但罪魁祸首是 athrx.sys
最终修正为 卸载设备并安装更新的官方驱动程序 摘自 Atheros 制造商网站。警告:蓝屏死机提示的模块可能是受影响子系统的一部分,而非源头。
典型的支持响应和用户快速步骤
在一次真诚的支持交流中,一位技术人员回答道: 很抱歉给您带来不便。这可能是驱动程序、内存或杀毒软件的问题。请更新您的驱动程序,如果问题仍然存在,请运行内存诊断程序。这是基本但有效的建议;但是,如果错误仍然存在,最好进一步使用验证程序和转储分析。
对于非技术用户,合理的协议是: 1)检查系统事件,2)更新关键驱动程序(芯片组/网络/图形),3)检查 RAM 使用集成工具,4)测试 引导 无需使用将钩子插入内核/GUI 的第三方软件进行清理,5)如果没有任何明确的地方,请在第三方驱动程序上使用验证程序。
驱动程序开发人员的最佳实践
如果你正在开发并偶然发现 D1/A,请检查 正在运行的例程未标记为可分页 在 DISPATCH_LEVEL 或更高级别运行时,请勿调用可分页函数。这包括避免引用分页区中的数据,并遵守 DDK 中描述的内核辅助函数的 IRQL 限制。
要同步共享数据, 应用规则“始终以相同的高 IRQL 访问共享数据” 并在适当的时候使用自旋锁。在多处理器上,IRQL 本身并不能保证不同 CPU 之间的互斥;自旋锁会提升 IRQL(至 DISPATCH_LEVEL)并协调不同核心之间的访问。如果需要操作敏感的硬件寄存器, KeSynchronizeExecution
帮助您以正确的 DIRQL 执行关键部分。
当计划需要提高 IRQL 时, 美国 KeRaiseIrqlToDpcLevel
对于 DISPATCH_LEVEL 或 KeRaiseIrql
小心,保存之前的 IRQL 并恢复它 KeLowerIrql
. 低于输入 IRQL,即使只是一瞬间, 这是一个严重的同步错误.
与中断和硬件的关系
IRQL 是 Windows 的机制 命令中断优先级和某些内部任务在架构层面,它与“中断”、“中断处理程序”或“中断优先级”等概念相关,在经典平台上,它与 可编程中断控制器(PIC)在其他系统中,优先级控制通过以下机制来表达: SPL en Unix的;总体思路是一样的:谁可以打断谁。
高级调试技巧
在堆栈指向的转储中 win32kfull!xxxProcessNotifyWinEvent
使用错误检查 0xA/0xD1,检查上下文 .process
y .thread
(如果可用),查看以下流程 explorer.exe
en !process 0 1
并检查覆盖层和 GUI 交互驱动程序。很多时候问题 这是在这条路线上出现的被第三方破坏的记忆.
不要忘记检查 IRQL !irql
,并进行对比: 如果你处于 DISPATCH_LEVEL (2) 并且参数 3 表示读/写/执行 在可分页的页面上,你已经知道它为什么会掉下来了。用 ln
在参数4中获取具体的函数。
明白了 什么是 IRQL? 以及它如何融入内核执行,有助于区分噪声和信号。如果你是用户,请关注 驱动程序和硬件 (默认使用 Verifier、事件和测试)。如果您进行开发,请严格遵循 IRQL、非分页内存和自旋锁同步的规则。使用正确的工具(WinDbg、Verifier)并仔细阅读参数(1、3 和 4), 这些错误检查不再是一个谜 并且它们成为可以有条不紊地解决的问题。
对字节世界和一般技术充满热情的作家。我喜欢通过写作分享我的知识,这就是我在这个博客中要做的,向您展示有关小工具、软件、硬件、技术趋势等的所有最有趣的事情。我的目标是帮助您以简单而有趣的方式畅游数字世界。