IRP 处理流程

来源:互联网 发布:java 数组接收 split 编辑:程序博客网 时间:2024/05/22 07:12

本文通过打开一个文件对象为例子,描述了一个带有两个I/O stack location的IRP的详细处理过程。当然一个IRP可以有多个I/O stack location,具体个数取决于将要处理该请求的驱动的层数。下图详细描述了驱动程序是如何通过使用I/O支持例程(IoXxx routines)来处理IRP以此来实现对相应文件的读写操作。




1:I/O管理器调用文件系统驱动(FSD)并把包含了子系统读/写请求的IRP传递给该驱动。 FSD访问IRP中的I/O stack location, 从而决定应该执行什么操作。


2:通过单此或者多次调用例程(IoAllocateIrp)来分配附加的IRP,FSD就可以把原始的请求分成几个小的请求(对于设备驱动不只一个的情况)。这些附加的IRP中的I/O stack location区域将会被下一级的驱动填充成零返回给FSD,正因为如此,FSD可以重复使用原始的IRP, 而不是向前面所讲的那样执行分配额外的IRP,并在这些IRP中创建针对下一层驱动的I/O stack location 并传送给下一层驱动。


3:对每一个驱动分配的IRP,如前面所述,FSD将调用I/O支持例程来注册一个由FSD提供的完成例程;在完成例程中,FSD可以判定下级驱动是否满足该请求,并且当下级驱动成功完成该IRP时释放该IRP。I/O管理器将调用FSD提供的完成例程来判断分配给驱动的IRP是成功完成了,还是完成了但有错误,或者是被取消了。上层驱动负责释放该驱动自己为下层驱动分配并设置的IRP。I/O管理器负责示释放它本身分配的并且被驱动完成的IRP。接下来,为了建立传递给下层驱动执行的请求,FSD将调用I/O支持例程(IoGetNextIrpStackLocation)来访问下层驱动的I/O stack location。(本例中,下一层驱动就是最后一层驱动)最后,FSD调用例程(IoCallDriver)将IRP传递给下一层驱动。


4:当IRP传递到下层驱动时,该驱动检查它对应的I/O stack location以此来判断自己在目标设备上该执行何种操作(由IRP_MJ_XXX之类的功能代码决定)。在指定的I/O stack location中已经把相应的目标设备通过设备对象抽象表征出来并出传递给本层驱动程序了。最底层的驱动可以做如下假设:I/O管理器把IRP转发给驱动的入口点,该入口点里面,驱动已经定义好了各种IRP_MJ_XXX操作(本例中是IRP_MJ_READ或者IRP_MJ_WRITE),而且上层的驱动已经对该请求的参数的有效性进行了检查。
如果没有上层驱动,那么底层驱动就要为针对IRP_MJ_XXX操作的参数的有效性进行检查,如果有效, 驱动程序通常调用I/O 支持例程来通知I/O管理器该层设备是将IRP挂起了,还是将IRP移入队列里等待,还是直接将IRP转发给由其他驱动提供的访问目标设备的驱动应用例程(此例中是一个物理或者逻辑设备: 磁盘或者分区)。

5: I/O管理器先判断驱动程序是否在忙于处理其他IRP,如果忙,则将IRP移入队列,并返回。否则,I/O管理器将把IRP转发给驱动提供的例程,该例程将在它所在的设备层发起I/O操作。

6:当设备中断发生时,驱动的中断服务例程(ISR)必须屏蔽相应中断并保存相关的上下文环境。然后,ISR将调用I/O支持例程(IoRequestDpc)将该IRP传递给由本层驱动提供的延时处理调用(DPC)例程中,以此使得该操作请求在比ISR低一级的硬件优先级下完成。

7:当驱动程序的DPC执行时,它使用上下文(由ISR中调用IoRequestDpc时传递下来)来完成I/O操作。 DPC调用相关的例程将下一个IRP(如果有的话)移出队列并将该IRP转发给由驱动提供的例程,这个例程开始在这个设备执行I/O操作。然后,DPC设置IRP中的I/O状态并调用例程IoCompleteRequest将它返回给I/O管理器。

8:I/O管理器把最底层的驱动程序的I/O stack location置零,然后调用文件系统已经注册好的完成例程(见步骤3)并把分配给FSD的IRP传递给文件系统驱动。该完成例程根据I/O状态(I/O status block)的结果来决定是重发该请求,还是更新原始请求保存的的内部状态信息,或者是释放分配给驱动的IRP。文件系统收集完所有分配给驱动的IRP中的状态信息,由此可以设置I/O的状态信息并结束原始IRP,当文件系统完成原始的IRP时,I/O管理器返回一个NTSTATUS的值给发起该I/O操作的原始请求者(子系统的原生函数)。


和上图描述的文件系统驱动一样,任何一个添加进驱动链表的新的驱动程序都可以实现下列功能:

●     在IRP中设置自己的完成例程。IoCompletion例程检查I/O status block来判断该IRP是被下一层的驱动成功完成了,还是被取消了, 或者是完成了但有错误. 完成例程还可以更新任何由驱动程序保存的IRP相关的状态, 也可以释放由驱动分配的任何操作相关的资源。另外,结束IRP之前,完成例程还可以延迟IRP的完成(通过告诉I/O管理器该IRP需要处理更多东西),并且可以在允许该IRP完成之前,向下一层驱动程序发送另外的请求。

●     在分配的IRP中可以创建下一层驱动对应的I/O stack location,并将请求发送给下一层驱动。

●     任何收到的IRP都可以通过在每一个IRP中添加针对下一层驱动的I/O stack location并调用IoCallDriver例程实现转发(值得注意的是: 针对主要功能代码为IRP_MJ_POWER,的IRP,驱动必须调用PoCallDriver例程才能转发)。

 

从上图我们可以知道,大部分驱动处理IRP是通过驱动提供的一组标准例程,但是处在驱动链中不同层的驱动必须有不同标准的例程。比如说,只有最底层的驱动可以处理来自物理设备的中断,所以只有最底层的驱动可以有ISR和DPC,ISR和DPC负责完成那些由中断驱动的I/O操作。从另一方面来说,因为最底层的驱动在收到来自设备的中断时就知道这些I/O操作已经完成,所以只有高层的驱动才有一个或者多个类似上文中提到的FSD的完成例程。



0 0
原创粉丝点击