驱动开发笔记2—PAGED_CODE(),IRQL,分页内存

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)
注意:
  1. 用户模式的代码(Ring3)一般都运行在PASSIVE_LEVEL

  2. 驱动程序的DriverEntry、IRP派遣函数一般都运行在PASSIVE_LEVEL,可以在必要时申请进入DISPATCH_LEVEL

  3. Windows负责线程调度的组件运行在DISPATCH_LEVEL,当前线程运行完时间片后,系统自动从PASSIVE_LEVEL提升到DISPATCH_LEVEL,当线程切换完毕后,系统又从DISPATCH_LEVEL回到PASSIVE_LEVEL。

  4. 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的程序中不允许使用分页内存

猜你喜欢

转载自blog.csdn.net/qq_42814021/article/details/120676748