IRQL = Interrupt Request Level.即中断执行的优先级。一个由windows虚拟出来的概念,划分在windows下中断的优先级,这里中断包括了硬中断和软中断,硬中断是由硬件产生,而软中断则是完全虚拟出来的。
IRQL = Interrupt Request.中断请求,当中断发生后,发生中断的设备通过它使用的中断请求信号线象中断控制器报告中断。CPU可以通过IRQ号来识别中断。
如果某个Interrupt产生了,且IRQL <= 目前Processor的IRQL Setting,那么将不会影响目前程序的运行。如果IRQL > 目前Processor的IRQL Setting,将执行中断程序,总而言之,较搞优先级的Interrupt中断较低优先级的Interrupt。通过函数KeGetCurrentIRQL()可以得到目前Processor的IRQL。可用的IRQL如下:
软件IRQL(Software IRQL )
- PASSIVE_LEVEL 0 // Passive release level
- LOW_LEVEL 0 // Lowest interrupt level
- APC_LEVEL 1 // APC interrupt level
- DISPATCH_LEVEL 2 // Dispatch level
硬件IRQL(Hardware IRQL )
- DIRQL: from 3 to 26 for device ISR
- PROFILE_LEVEL 27, 0x1B // Timer used for profiling.
- CLOCK1_LEVEL 28, 0x1C // Interval clock 1 level - not used on x86
- CLOCK2_LEVEL 28, 0x1C // Interval clock 2 level
- SYNCH_LEVEL 28, 0x1C // Synchronization level
- IPI_LEVEL 29, 0x1D // Interprocessor interrupt level
- POWER_LEVEL 30, 0x1E // Power failure level
- HIGH_LEVEL 31, 0x1F // Highest interrupt level
数值越大代表其IRQL的优先级越高。在驱动开发中,常见的由以下三个
PASSIVE_LEVEL
DISPATCH_LEVEL,
DIRQL
使用SpinLock的可解決在多個驅動程式中同步的問題。DDK提供了兩組function。
- KeAcquireSpinLock()
- KeReleaseSpinLock()
及
- KeAcquireSpinLockAtDpcLevel()
- KeReleaseSpinLockAtDpcLevel()
根據DDK上所說,KeAcquireSpinLock()僅能在IRQL < = DISPATCH_LEVEL的狀況下執行。不過,如果想在DISPATCH_LEVEL來執行,最好還是用 KeAcquireSpinLickAtDpcLevel(). DDK上也說了,不要太過份依賴SpinLock作為資料同步的機制。原因在於KeAcquireSpinLock()一旦被呼叫,將會導致目前的 IRQL LEVEL提升至DISPATCH_LEVEL。此時,Windows Dispatcher的scheduler將會無法被執行。導致系統正常的response變慢。因此,在IRQL已經位於DISPATCH_LEVEL 時,就應該呼叫KeAcquireSpinLockAtDpcLevel()。實際上,KeAcquireSpinLockAtDpcLevel()什麼 也沒有作。因為,在DISPATCH_LEVEL時,所有routine的執行已經是同步了。所以沒有進一步進行SpinLock()的必要了。 如果是在IRQL < DISPATCH_LEVEL的狀況下呢?這個時候去呼叫KeAcquireSpinLock(),將會導致企圖將IRQL降低到DISPATCH_LEVEL的狀況發生。 而routine自行降低IRQL是不可以的。
今天来看看驱动中常见的几种IRQL。每个NDIS API函数,在DDK文档中都描述了它的IRQL.
PASSIVE_LEVEL:
IRQL最低级别,没有被屏蔽的中断(即中断所有IRQL的中断),在这个级别上,线程执行用户模式,可以访问分页内存。DriverEntry(), AddDevice()和大部分IRP Dispatch函数都运行在PASSIVE_LEVEL。
APC_LEVEL:
在这个级别上,只有APC级别的中断被屏蔽(即中断除了IRQL=APC_LEVEL的中断),,可以访问分页内存。当有APC发生时,处理器提升到APC级别,这样,就屏蔽掉其它APC,为了和APC执行一些同步,驱动程序可以手动提升到这个级别。比如,如果提升到这个级别,APC就不能调用。在这个级别,APC被禁止了,导致禁止一些I/O完成APC,所以有一些API不能调用。另外,系统在APC_LEVEL处理缺页中断,所以,执行在>=APC_LEVEL上的代码必须存放在NON-PGAE内存中。
DISPATCH_LEVEL
这个级别,DPC 和更低的中断被屏蔽,不能访问分页内存,所有的被访问的内存不能分页。因为只能处理分页内存,所以在这个级别,能够访问的Api大大减少。StartIo(),DPC函数和许多其他函数都运行在DISPATCH_LEVEL。这是因为,他们要存取device object和device extension的内容,这样克以不被IRP Dispatch routines所抢占。
DIRQL (Device IRQL)
一般的,更高级的驱动在这个级别上不处理IRQL,但是几乎所有的中断被屏蔽,这实际上是IRQL的一个范围,这是一个决定某个驱动有更高的优先级的方法。thread dispatch(线程调度)函数运行在这个LEVEL上。所以,运行在>=DISPATCH_LEVEL的线程将独占处理器,除非有比他高LEVEL的中断产生。
说明:如果要共享数据,那么所有对该数据存取的活动都要安排在高于PASSIVE_LEVEL的同一个IRQL上。这样,在单CPU环境下,就能有效地避免共享冲突。