当驱动程序处理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,当下层的驱动完成这个请求的时候会调用这个例程,而往往在这个例程里,就是简单的触发一下一个事件。
另外可以顺便提一下Linux。Linux 2.6也有类似的中断机制,它有更多的软中断优先级,即不同优先级的softirq,而类似于DPC,Linux也提供了专门的软中断,对应DPC的就是tasklet。Linux没有一个像windows这么一致的层次驱动程序架构,所以它的异步I/O稍微粗糙一些,主要是通过以前的一些阻塞点,现在直接返回-EIOCBRETRY,而让调用者在合适的时机继续重试。在这个方法中,可以认为整个操作由一个函数完成,每次操作有进展时,都把这个函数从头执行一遍,当然已经完成的部分就不会再有实际的I/O。这样的最大好处是原有的文件系统和驱动程序不用完全重写。而对于同步调用,只要阻塞就可以了,这样对系统的修改较小。这时候,要提供POSIX aio的语义,就可能需要提供一些用户线程来完成重试的过程了(回想Windows可以通过中断和DPC完成的)。而对于Solaris,也是类似的处理,如果设备支持异步I/O,那就通过中断可以完成,否则就使用内部的LWP来模拟。
以上内容摘自
http://blog.csdn.net/emag_cpp/archive/2005/02/01/276301.aspx