IO管理器的任务就是管理IO,本质上windows的IO操作都是异步的,这是由IO流的分层下递和IRQL共同决定的,效果就是IO流被处 理的每一个步骤都可能在任意线程上下文中进行,那么如果最下面的驱动完成了一个irp,这个怎么让上面的驱动知道呢?当然方法很多,比如上层驱动等待在一 个event上,而microsoft的作法显然更好,就是为每一个irp流经的每一个设备提供了一个选择,该设备的驱动可以提供一个回调函数,以便下层 驱动完成请求的时候通知该层,这就是IO完成回调函数,按照常规理解,这个函数应该由下层的驱动调用,而实际上这样做的话,实现就不是那么“结构化”了, 驱动的开发者就必须负责去手工调用该函数,失去了回调本来的含义,增加了驱动开发者的负担,因此这个回调函数就由IO管理器来调用,这里的说法仅仅是按照 模块化思想来的,在整个windows内核中你实际上看不到一个叫做IO管理器的模块的,所谓的IO管理器就是一系列函数接口,对于IO完成回调函数的调 用,你只需要调用一个IofCompleteRequest就可以了,而这个接口的实现就是IO管理器的重要内容,那么它是怎么实现的呢?如果不看 ReactOS的代码,也不反汇编windows内核的话,还有一种情况能够得到它的实现,那就是自己想,我就是这么干的,没想到还真蒙对了。
在分层的驱动逐次往下层的设备递交irp的过程中,只要有一个分发函数调用了IofCompleteRequest,那么该irp就不会再往下走了,而是 顺着来的方向的反方向逐个的调用irp栈上的IO完成回调函数,因此IofCompleteRequest的实现首先就是遍历所有的irp栈帧,然后在每 一个上执行其IO完成回调函数即可,这只是最简单的方式,windows内核的逻辑也是这么来的,但是往往自己能想到的都是最简单的,扩展开来的话会有很 多自己想不到的,比如有一种情况就是irp栈某一层完成了io,IO管理器调用了上层的io完成回调,接下来该回调返回以后,IO管理器会接着调用上上层 的io完成回调,然而如果这个上层的io完成回调中如果一个数据没有准备好,而上上层io完成回调中又需要这个数据的话,这就会引起问题,IO管理器直接 调用了上上层的io完成回调,而实际上这个上层的io还没有完成呢(一个数据没有到位)。以上这种情况是很常见的,毕竟windows内核是一个本质上异 步的操作系统内核,一个irp到达某个层次后可能为了实现某项功能会进行一些别的操作,比如查询一些信息,比如和别的内核模块进行交互,这都会产生另外的 irp,而这些irp是和上层没有关系的,是在本层产生并下发的,因此如果这些irp不完成,那个本来的irp也就算没有完成,因此虽然下层完成了这个本 来的irp,只因为这些额外的我们自己在本层生成的irp还没有完成,这就意味着IO完成不能这么简单的逆流而上,实际上最简单的方式就是告诉IO管理 器,也就是告诉IofCompleteRequest不要继续逆流而上了,等到时机成熟的时候,我自己逆流而上,这就是IO完成回调函数 STATUS_MORE_PROCESSING_REQUIRED返回值的含义,IofCompleteRequest一旦接收到某个回调函数的返回值是 这个,那么就终止循环,然后退出IO管理器,等到这个层次的所有数据都准备好之后,该驱动自行调用IofCompleteRequest,这将重复同样的 过程,只是起点变化了,不是最底层的驱动了,而是该层驱动,也就是因为数据而等待的驱动,数据可以在io完成中被准备好,也可以是别的,这就是另外的话题 了。
最终irp会回到它生成的地方,到这里之后,大多数情况下仍然是任意线程上下文,因为最底层驱动的完成是靠中断触发,所以一系列的逆流而上就是在DPC中 执行的(DPC在cpu的队列中排队,为了延迟执行硬中断任务,而APC在线程队列中排队,执行线程上下文中执行的任务),因此为了将完成消息通知到发出 请求的线程,就需要将最后的irp层次的io完成回调函数排入到该线程的apc队列中,然后最终在该线程的上下文中被执行,实际上排入apc队列的是 IopCompleteRequest函数。底层驱动不能立马完成的irp,返回时一定要设置pending状态,这样上层才可以正确处理完成回调函数, 注意,这个pending和STATUS_MORE_PROCESSING_REQUIRED的含义是不同 的,STATUS_MORE_PROCESSING_REQUIRED是自己来处理接下来的完成回调,而pending则是指示io还没有完成,可是完成 回调却要调用,为了防止阻塞,真正的完成操作将在dpc/apc中进行。
本文转自 dog250 51CTO博客,原文链接:http://blog.51cto.com/dog250/1273452