selph
selph
发布于 2021-12-03 / 1004 阅读
0
0

Windows 内核编程 ch6--内核机制

本章介绍了IRQL以及和IRQL相关的一些内容,算是对我知识空缺的一个填补,自此知道了IRQL是怎么一回事

6.1 中断请求级别

硬件设备完成某些事情之后,会通过请求中断的方式通知处理器,中断会连接到中断处理器硬件上,然后将请求发往处理器去执行对应的中断处理程序 ISR(中断服务例程 Interrupt Service Routine)

每个硬件与一个优先级相关联,称为中断请求级别 IRQL,由 HAL 决定,IRQL 在每个处理器上下文都有,就像每个处理器都有一套寄存器一样

IRQL 提供的规则是,处理器执行有最高 IRQL 的代码,也就是说提高 IRQL 会临时阻止小于等于当前 IRQL 的代码的执行

当执行当前线程的时候,发生了 IRQL 等级比较高的中断的时候,会立马中断当前线程保存上下文切换去执行 ISR,书上的两个图:

image.png

所有的 ISR 都是运行在最初被中断的线程上的,当有更高级别的中断出现,就会再次中断当前的 ISR

被中断的线程不会因为中断而减少原有的时间片,中断处理结束后,就好像一切都没发生一样

用户模式编程的时候不会提到 IRQL 这个概念,因为在用户模式下 IRQL 始终为 0 且无法更改,但在内核模式下可进行修改,重要的 IRQL 等级说明如下:

  • PASSIVE_LEVEL(0):CPU 的正常 IRQL,线程调度正常进行
  • APC_LEVEL(1):用于特殊内核 APC,线程调度正常进行
  • DISPATCH_LEVEL(2):这是发生变化的地方,调度器不会被唤醒,不允许访问分页内存,不允许在内核对象上等待,否则会系统崩溃
  • 设备 IRQL:用于硬件的中断范围,在 x64/ARM/ARM64 上是 3-11,在 x86 上是 3-26,规则与 IRQL2 一样
  • HIGH_LEVEL:最高级别,屏蔽一切中断,被一些进行链表操作的 API 所使用,在 x64/ARM/ARM64 上是 15,在 x86 上是 31

image.png

当 IRQL>=2 的时候,对执行的代码会有一些限制:

  • 访问不在物理内存中的内存地址是一个致命错误,尽量避免使用分页池内存和用户缓冲区数据
  • 等待任何调度器内核对象都会引起系统崩溃,除非将等待时间设置为 0

这些限制是因为调度程序在 IRQL=2 上运行,当 IRQL>=2 的时候,调度程序将无法在该处理器上唤醒,不会发生上下文切换

内存被交换到页面文件后,不存在于物理内存中,访问该内存会触发 page fault 中断,由此中断将相应的内存从页面文件交换回物理内存

因为 page fault 中断的级别 IRQL = 1,因此,当代码执行在 IRQL=2 的时候,不会对 page fault 进行处理,导致无法访问到目标内存地址,从而引发系统奔溃

windbg 可以使用 !irql 命令查看 IRQL 等级,可以使用 !idt 查看已注册的中断

提升和降低 IRQL

在内核模式下,可以手动提高(API:KeRaiseIrql) 和降低(API: KeLowerIrql) IRQL

如果提高了 IRQL,要确保在同一函数内将 IRQL 等级降低回来,低入高返回,容易导致系统奔溃

线程优先级和 IRQL

IRQL 是处理器的属性,线程优先级是线程的属性,线程优先级仅在 IRQL<2 时才有意义

如果某个线程将 IRQL 提高到了 2,理论上它就有了无限的时间片,直到 IRQL 从 2 降低下来为止

任务管理器使用一个叫做系统中断的伪进程来显示花费在 IRQL>=2 时的 CPU 时间

image.png

Process Explorer 上使用 Interrupts 伪进程来显示:

image.png

6.2 延迟过程调用

书中示例是 ReadFile 函数的调用过程,该函数进入内核后,通过 IO 管理器构造 IRP 派遣给对应驱动程序之后,驱动程序给硬件设备下达工作任务,自此请求线程可以继续做其他的事情,硬件的活它自己干,硬件完成任务之后,会触发硬件中断,将处理结果通知给处理器

硬件触发的中断在设备 IRQL 上进行(中断是异步到达的)

image.png

驱动处理请求完成之后,会调用 IoCompleteRequest 来完成请求返回结果,该函数只能在 IRQL<=2 时调用,ISR 不能直接调用该函数 (下一章介绍具体原因)

DPC 是一个对象,封装了一个函数,函数