当驱动程序处理IRP的时候,它可能立刻完成,也可能在中断里才能完成,比如说,往硬件设备发出一个请求(通常可以是写I/O port),当设备完成操作的时候会触发一个中断,然后在中断处理函数里得到操作结果。Windows有两类中断,硬件设备的中断和软中断,分成若干个不 同的优先级(IRQL)。软中断主要有两种:DPC(Delayed Procedure Call)和APC(Asynchronous Procedure Call),都处于较低的优先级。驱动程序可以为硬件中断注册ISR(Interrupt Service Routine),一般就是修改IDT某个条目的入口。同样,操作系统也会为DPC和APC注册适当的中断处理例程(也是在IDT中)。值得指出的 是,DPC是跟处理器相关的,每个处理器会有一个DPC队列,而APC是跟线程相关的,每个线程会有它的
APC队列(实际上包括一个Kernel
APC队列和User
APC队列,它们的调度策略有所区别),可以想象,APC并不算严格意义上的中断,因为中断可能发生在任何一个线程的上下文中,它被称为中断,主要是因为 IRQL的提升(从PASSIVE到APC),APC的调度一般在线程切换等等情形下进行。当中断发生的时候,操作系统会调用中断处理例程,对于硬件设备 的ISR,一般处理是关设备中断,发出一个DPC请求,然后返回。不在设备的中断处理中使用太多的CPU时间,主要考虑是否则可能丢失别的中断。由于硬件 设备中断的IRQL比DPC中断的高,所以在ISR里面DPC会阻塞,直到ISR返回IRQL回到较低的水平,才会触发DPC中断,在DPC中断里执行从 硬件设备读取数据以及重新请求、开中断等操作。ISR或者DPC可能在任何被中断的线程上下文(arbitrary thread context)执行,事实上线程的上下文是不可见的,可以认为是系统借用一下时间片而已。
总的来说,Windows的异步I/O架构中,主要有两种动力,一是发起请求的线程,一部分内核代码会在这个线程上下文执行,二是ISR和DPC,这部分 内核代码会在中断里完成,可能使用任何一个线程的上下文。而调度常见使用回调和事件(KEVENT),比如说在往下一层的驱动程序发出请求的时候,可以指 定一个完成例程Completion Routine,当下层的驱动完成这个请求的时候会调用这个例程,而往往在这个例程里,就是简单的触发一下一个事件。