【原创】浅说windows下的中断请求级IRQL
一 中断分类
根据中断源不同,可以将中断分为
-
硬件中断:硬件上产生的中断,可以来自处理器的内部和外部。处理器的外部中断可以来自各种PIN信号接口和Local APIC的LINT0和LINT1引脚,以及外部的I/O APIC发送过来的中断信息。
-
软件中断:软件上产生的中断,使用INT指令主动发起的中断,如INT 0X40,调用0X40号的中断服务例程。
硬件中断源又可以分为
- 可屏蔽中断:可屏蔽的中断如下
-
通过处理器的INTR pin接收的中断请求,典型地,INTR连接到8259A PIC上,在支持APIC的处理器上,LINT0被作为INTR连接到外部中断控制器上
-
通过Local APIC产生的本地中断源
-
来自芯片组上的I/O APIC产生的中断信息
- 不可屏蔽中断:通过处理器NMI pin接收的中断请求是不可屏蔽的,在支持APIC的处理器上INTR1 pin被作为NMI pin使用,接收来自外部的NMI信号
二 中断控制器
- 以前的8259A PIC(8259可编程中断控制器)
在单处理器上,处理器的INTR pin接收来自外部8259中断控制器传送过来的中断请求,其位于PCI-to-ISA bridge(南桥)芯片的LPC控制器里。
![8259A](http://cl.ly/image/1p352Q2K3x2J/8259%E7%BA%A7%E8%81%94.png)
每个8259A PIC的IR口都连接着一条IRQ线。主片的IR0到IR7对应着IRQ0到IRQ7线,但是IR2连接着从片的INTR pin。从片的IR0到IR7对应着IRQ8到IRQ15线。由于从片连接到主片的IR2上,所以从片的IR1同时连接到IRQ2和IRQ9。
在8259A中,主片IR0的中断请求优先级最高,主片IR7最低,从片IR0-7所有中断请求优先级都相当于IRQ2。所以IRQ线的优先级由高到低次序为IRQ0,IRQ1,IRQ8-15,IRQ3-7。
- 现在的中断控制器:APIC
为了适应多处理器,Intel在Pentium处理器开始引入了APIC(Advanced Programmable Interupt Controller)机制。
APIC经历了4个版本,82489DX芯片,APIC,xAPIC,x2APIC。xAPIC共有256个IRQ线,而x2APIC比xAPIC多了256个,总共512条IRQ线。
![APIC](http://cl.ly/image/1h2t273q280e/36b37900-4442-4aa4-9395-6e41f1405ef0.png)
三 中断请求级IRQL
在APCI中,每个IRQ都有各自的优先级,一个正在运行的线程可能被中断打断,进入到中断处理函数,当遇到优先级更高的中断,处在低优先级的中断也会被打断,进入到更高级的中断处理函数。
windows将中断的概念进行了扩展,提出了中断请求级的概念,数字低的优先级高,其中不仅包括了APIC的所有中断,也包括了3个软件中断。
![中断](http://cl.ly/image/0b2h112H3m45/f5f3b37c-3563-423e-8119-56c1b24efd9b.jpg)
用户模式的代码运行在最低优先级PASSIVE_LEVEL。驱动中的DriverEntry,派遣函数,AddDevice等函数一般运行在PASSIVE_LEVEL,在必要的时候可申请进入DISPATCH_LEVEL函数。
需要特别注意的是,windows负责线程调度的组件是运行在DISPATCH_LEVEL级别,当前的线程运行完时间片后,系统自动从PASSIVE_LEVEL级别提升到DISPATCH_LEVEL级别。当线程切换完毕后,操作系统又从DISPATCH_LEVEL降到PASSIVE_LEVEL。驱动程序的StartIO函数和DPC函数也运行在DISPATCH_LEVEL级别。在内核模式,可以通过KeGetCurrentIrpl内核函数来得到当前的IRQL级别。
四 代码中断级
PASSIVE_LEVEL比DISPATCH_LEVEL低,在实际编程中,许多具有比较复杂功能的内核API都要求在PASSIVE下运行,而只有比较简单的API能在DISPATCH级执行。
在调用任何一个内核API前,必须查看WDK文档,了解这个内核API的中断要求
中断级的简单判断方法
如何判断我们正在编写的代码的中断级呢?暂时可以使用下面规则来处理。
- 规则1:如果在调用路径没有特殊情况(导致中断级的提高或降低),则一个函数的中断级与调用源的中断级相同;
- 规则2:如果在调用路径上有获取自旋锁,则中断级随之升高;如果有释放自旋锁,则中断级随之降低。
如果当前代码运行在DISPATCH级,而我们又必须调用PASSIVE级的内核API,使用内核API强制降低当前的中断请求级是不被允许的,windows的代码都运行在规范的中断级上,任意降低中断级都会导致不可预料的后果。
这样的问题有很多种解决方法,比如生成一个专门的线程去执行PASSIVE级的代码。