PAGED_CODE
PAGED_CODE()是DDK提供的宏,它会检验当前程序的IRQL。
如果IRQL >= DISPATCH_LEVEL, PAGED_CODE()会导致系统断言。
IRQ 和 IRQL
中断请求(IRQ)分为硬件中断(外部中断)和软件中断(内部中断,int n指令产生的中断)。
硬件中断分为不可屏蔽中断(NMI)和可屏蔽中断(INTR),其中,可屏蔽中断是通过可编程中断控制器(PIC)8259A芯片向CPU发出请求的。
高级可编程控制器(APIC)定义的IRQ的数量有24个。
每个IRQ都有自己的IRQL(中断请求级别),正在运行的线程随时可以被中断打断,进入到中断处理程序。而当优先级高的中断来临时,处在优先级低的中断处理程序也会被打断,进入到更高级别的中断处理函数。
Windows规定了32个IRQL,
0-2 级为软件中断
3-31 级为硬件中断,其中3-26为APIC中的24个硬件中断
硬件的IRQL称为设备中断请求级(DIRQL)。
当硬件中断来临的时候,操作系统提升IRQL到DIRQL级别,并且运行中断处理函数。中断处理函数结束后,操作系统把IRQL降到原来的级别。
#define PASSIVE_LEVEL 0
#define LOW_LEVEL 0
#define APC_LEVEL 1
#define DISPATCH_LEVEL 2
...
#define PROFILE_LEVEL 27
#define CLOCK1_LEVEL 28
#define CLOCK2_LEVEL 28
#define IPI_LEVEL 29
#define POWER_LEVEL 30
#define HIGH_LEVEL 31
#define SYNCH_LEVEL (IPI_LEVEL-1)
注意:
-
用户模式的代码(Ring3)一般都运行在PASSIVE_LEVEL。
-
驱动程序的DriverEntry、IRP派遣函数一般都运行在PASSIVE_LEVEL,可以在必要时申请进入DISPATCH_LEVEL。
-
Windows负责线程调度的组件运行在DISPATCH_LEVEL,当前线程运行完时间片后,系统自动从PASSIVE_LEVEL提升到DISPATCH_LEVEL,当线程切换完毕后,系统又从DISPATCH_LEVEL回到PASSIVE_LEVEL。
-
IRQL和线程优先级不一样,线程优先级只针对应用程序而言,线程优先级高的线程有更多机会被内核调度。
相关函数:
// 获得当前的IRQL级别
KIRQL
KeGetCurrentIrql(VOID)
{
return KeGetPcr()->Irql;
}
PKPCR
KeGetPcr(VOID)
{
return (PKPCR)__readgsqword(FIELD_OFFSET(KPCR, Self));
}
// 提高IRQL
VOID
KeRaiseIrql(
IN KIRQL NewIrql,
OUT PKIRQL OldIrql
);
// 恢复IRQL
VOID
KeLowerIrql(
IN KIRQL NewIrql
);
例如:
KIRQL OldIrql;
ASSERT(KeGetCurrentIrql() < DISPATCH_LEVEL);
KeRaiseIrql(DISPATCH_LEVEL, &OldIrql);
KeLowerIrql(OldIrql);
分页内存
再不得不提一下分页内存。
使用分页内存可能会导致页故障,因为分页内存随时可能从物理内存交换到磁盘文件。读取不在物理内存中的分页内存时,就会引发页故障,从而执行对应的异常处理函数,异常处理函数会重新将磁盘文件的内容交换到物理内存中。
页故障在IRQL >= DISPATCH_LEVEL的程序中会导致系统崩溃(蓝屏死机)。
因此,IRQL >= DISPATCH_LEVEL的程序中不允许使用分页内存。