1. IRQL 基础概念:中断请求级别

1.1 什么是 IRQL

IRQL(Interrupt Request Level,中断请求级别)是 Windows 内核中用于优先级化中断处理与线程调度的核心机制。每个处理器核心在任意时刻都运行在某个 IRQL 上,系统通过比较当前 IRQL 与中断源的 IRQL 来决定是否允许该中断发生。

IRQL 的本质是一个整数优先级,数值越高,优先级越高。当处理器运行在 IRQL X 时,只有 IRQL 高于 X 的中断才能被响应,低于或等于 X 的中断被屏蔽。

关键理解:IRQL 不是线程优先级,而是处理器执行环境的优先级。线程调度只发生在 PASSIVE_LEVEL,一旦 IRQL 提升到 DISPATCH_LEVEL 或更高,线程调度被禁止,当前代码必须快速完成。

1.2 Windows IRQL 等级划分

Windows 在 x64 架构上定义了从 0 到 15 的 IRQL 等级(x86 为 0–31,但常用级别一致)。下表列出驱动开发中最常遇到的级别:

IRQL 名称 数值(x64) 执行环境 允许的操作 典型场景
PASSIVE_LEVEL 0 普通线程 所有操作(分页、I/O、同步、线程调度) 大多数驱动 Dispatch 例程、用户请求处理
APC_LEVEL 1 异步过程调用 可访问分页内存(需小心),禁止线程调度 APC 回调、某些 I/O 完成处理
DISPATCH_LEVEL 2 DPC / 调度器 禁止分页、禁止线程调度、只能使用非分页内存 DPC 例程、定时器回调、调度器内部
PROFILE_LEVEL 27 性能采样 极简操作,仅用于性能分析 Profile 中断处理
CLOCK_LEVEL 28 时钟中断 极简操作,仅用于时钟维护 系统时钟中断处理
IPI_LEVEL 29 处理器间中断 极简操作,用于多核通信 TLB 刷新、跨核通知
POWER_LEVEL 30 电源故障 极简操作,仅用于电源紧急处理 掉电、休眠紧急路径
HIGH_LEVEL 31 最高级别 屏蔽所有中断,仅用于最紧急操作 机器检查、不可屏蔽中断(NMI)
驱动开发黄金法则:在 DISPATCH_LEVEL 及以上,绝对不能访问分页内存、等待线程同步对象、或触发页面错误。违反此规则会导致系统崩溃(BugCheck 0xA 或 0xBE)。

1.3 IRQL 与线程调度的关系

线程调度器(Dispatcher)只能在 PASSIVE_LEVEL 运行。当 IRQL 提升到 DISPATCH_LEVEL 或更高时,调度器被禁止执行,这意味着:

  • 当前线程不会被抢占——即使更高优先级的线程就绪,也必须等待 IRQL 降回 PASSIVE_LEVEL。
  • 无法进行上下文切换——任何尝试等待(如 KeWaitForSingleObject)都会导致系统崩溃。
  • DPC 例程运行在 DISPATCH_LEVEL,因此 DPC 中不能等待、不能获取需要调度的锁。

下图展示了 IRQL 与线程调度、中断响应的层级关系:

IRQL 层级与线程调度关系图 IRQL 层级与线程调度关系 HIGH_LEVEL (31) POWER_LEVEL (30) IPI_LEVEL (29) CLOCK_LEVEL (28) PROFILE_LEVEL (27) ▲ 中断屏蔽区(不可调度) DISPATCH_LEVEL (2) DPC 例程、调度器内部(禁止分页、禁止等待) APC_LEVEL (1) APC 回调(可访问分页内存,不可调度) PASSIVE_LEVEL (0) 普通线程执行、用户请求处理(可调度、可分页) IRQL 规则 • 高 IRQL 屏蔽低 IRQL 中断 • 线程调度仅在 PASSIVE_LEVEL • DISPATCH_LEVEL 以上禁止分页 中断发生 → IRQL 提升 IRQL 恢复

1.4 IRQL 提升与恢复的典型流程

驱动代码在执行过程中会经历 IRQL 的升降。以下是一个典型场景:

  1. 用户态调用 → 系统服务进入内核,IRQL 为 PASSIVE_LEVEL。
  2. Dispatch 例程 → 驱动收到 IRP,仍在 PASSIVE_LEVEL 执行。
  3. 请求 I/O → 驱动调用 KeRaiseIrql(DISPATCH_LEVEL, ...) 提升 IRQL 以访问硬件寄存器。
  4. DPC 回调 → I/O 完成后,DPC 例程在 DISPATCH_LEVEL 执行。
  5. IRQL 恢复 → DPC 返回后,系统自动将 IRQL 降回 PASSIVE_LEVEL。
常见 BugCheck 场景:如果驱动在 DISPATCH_LEVEL 尝试访问分页内存(如调用 ProbeForRead 或访问分页池),会触发 BugCheck 0xBE(ATTEMPTED_WRITE_TO_READONLY_MEMORY)或 0xA(IRQL_NOT_LESS_OR_EQUAL)。WinDbg 分析时,!irql 命令可快速确认当前 IRQL。

1.5 IRQL 与同步机制

不同 IRQL 下可用的同步原语有严格限制:

同步原语 PASSIVE_LEVEL APC_LEVEL DISPATCH_LEVEL ≥ CLOCK_LEVEL
Mutex / Fast Mutex ✅ ❌ ❌ ❌
Spin Lock(自旋锁) ✅ ✅ ✅ ❌
Push Lock(共享/排他) ✅ ✅ ✅ ❌
KeWaitForSingleObject ✅ ❌ ❌ ❌
Interlocked 操作 ✅ ✅ ✅ ✅
调试技巧:在 WinDbg 中使用 !irql 命令查看当前处理器 IRQL。如果看到 DISPATCH_LEVEL 且系统卡死,通常意味着 DPC 超时或死锁。使用 !dpcs 可列出所有待处理的 DPC。

1.6 小结

  • IRQL 是 Windows 内核的中断优先级机制,数值越高优先级越高。
  • 驱动开发主要关注 PASSIVE_LEVEL、APC_LEVEL、DISPATCH_LEVEL 三个级别。
  • 线程调度只在 PASSIVE_LEVEL 发生,DISPATCH_LEVEL 及以上禁止调度和分页。
  • 违反 IRQL 规则是驱动崩溃的首要原因之一,WinDbg 的 !irql 和 !analyze -v 是排查利器。