IRQL 是 Windows 给 CPU 的一种机制,不会被等级小于等于当前 IRQL 的中断请求而中断,以达到同步处理的需要。
IRQL 简介
IRQL 中断请求级别是操作系统赋予 CPU 的一种机制,不是来自 CPU 的,是 Windows 系统上最重要的性能机制之一。
中断请求 IRQ 一般有两种,一种是外部中断(硬件产生的中断),另一种是软件中断(Int 3 断点中断,int 1 单步中断等),后来 Windows 操作系统将中断进行了扩展,提出了中断请求级的概念,其中规定了 32 个中断请求级别,分别是 0-2 级别为软件中断,3-31 为硬件中断。
驱动编程中通常接触到的是 0 到 2 的 IRQL 以及 DIRQL:
IRQL | IRQL 数值:x86 | IRQL 数值:AMD64 | IRQL 数值:IA64 | 描述 |
---|---|---|---|---|
Passive Level | 0 | 0 | 0 | 应用层线程和大部分内核函数处于该 IRQL,可以无限制使用所有内核 API,可以访问分页以及分分页内存 |
APC Level | 1 | 1 | 1 | 异步方法调用(APC)或页错误时处于该 IRQL,可以使用大部分内核 API,可以访问分页以及分分页内存 |
Dispatch Level | 2 | 2 | 2 | 延迟方法调用(DPC)时处于该 IRQL,可以使用特定的内存 API,只能访问非分页内存 |
如果某个中断产生了,如果中断级别比当前 IRQL 低,那么该中断不会影响当前程序运行,如果中断的 IRQL 比当前 CPU 的 IRQL 大,则会中断当前的中断
可通过 KeGetCurrentIRQL()
获取当前 IRQL 等级
说明:如果要共享数据,那么所有对该数据存取的活动都要安排在高于 PASSIVE_LEVEL 的同一个 IRQL 上。这样,在单 CPU 环境下,就能有效地避免共享冲突。
IRQL 的提升与降低
有些时候驱动程序中需要提升 IRQL 级别,在运行一段时间后,再将降回原来的 IRQL 级别,这样做的目的一般是基于同步处理的需要。
然后驱动程序使用内核函数 KeRaiseIrql
将 IRQL 提高。
KeRaiseIrql
需要两个参数,第一个数是提升后的 IRQL 级别,第二个参数是保存提升前的 IRQL 级别。
最后,驱动程序某个时刻需要将 IRQL 恢复到以前的 IRQL 级别,驱动程序可以调用 KeLowerIrql
内核函数。
下面代码演示了驱动中如何提升和降低 IRQL 的代码:
VOID RaiseIRQL_Test()
{
KIRQL oldirql;
// 确保当前 IRQL 等于或小于 DISPATCH_LEVEL
ASSERT(KeGetCurrentIrql() <= DIPATCH_LEVEL);
// 提升 IRQL 到 DISPATCH_LEVEL,并将先前的 IRQL 保存起来
KeRaiseIrql(DISPATCH_LEVEL, &oldirql);
//...
// 恢复到先前的 IRQL
KeLowerIrql(oldirql);
}
分页内存和非分页内存
Windows 操作系统定制了两种内存:
- 分页内存:可以置换到硬盘上
- 非分页内存:不能置换到硬盘上
调用 API 访问内存发生错误一般是 IRQL 出现错误了
要注意,DisPatch_Level(DPC_Level) 只能用非分页内存(错误 0x0000007E),因为在这个 IRQL 下,发生缺页中断的时候,因为缺页中断等级相同而不会进行触发,导致读取地址内容不在内存中,无法从硬盘置换出来,从而导致蓝屏
数值越低,优先级越低:DPC>APC> 大部分 API
参考资料:
IRQL 的理解和认识_`Nobody 的博客-CSDN 博客_irql
IRQL 深入解析--IRQL 级别_首席技术总监的专栏-CSDN 博客_irql