驱动IRP是什么?

Windows驱动中的IRP有哪些部分组成

如下图,Windows驱动中的IRP可以认为是由两个部分组成的:一个”固定”部分和一个I/O栈。固定部分包含这个驱动请求的相关信息,有可能不同驱动中的IRP的固定部分相同,也可能在不同驱动间传递IRP时不需要关注它。

假如某个驱动要处理这个IRP,那么I/O栈就包含被指定给它的信息。驱动栈中有几个要处理这个IRP的驱动,那么I/O栈中将至少有几个I/O栈位置。

为避免每一个IRP都要从NT的非分页池中分配,I/O管理器维护一对保存有预分配的IRP的旁观列表(lookaside list,其相关知识会专门加以讨论)。NT V4.0中,其中一个旁观列表保存带有一个单一I/O栈位置的IRP。另外一个旁观列表保存带有三个I/O栈位置的IRP。I/O管理器总是尽可能地使用这些旁观列表中的IRP。

因此,只有没有可用的IRP,或者要分配的IRP所需的I/O栈位置超过三个时,I/O管理器才会从非分页池中分配IRP(关于内存分配的问题会专门讨论)。否则,它会尽量使用旁观列表中的IRP。
Windows驱动之IRP结构示意图

IRP的固定部分中需要特别关注或有用的域如下:

  • MdlAddres, UserBuffer和AssociatedIrp.SystemBuffer – 如果有一个关联I/O操作请求者的数据buffer,则这三个域被用来描述这个buffer。后面会加以详述。
  • Flags – 顾名思义,这个域包含描述I/O请求的标记。例如,如果在此域中设置了IRP_PAGING_IO标记,则表示IRP所描述的读或写操作是一个分页请求。以此类推,如果设置了IRP_NOCACHE则表示这个请求是在没有中间buffer的情况下被处理的。
  • IoStatus – 当IRP被完成时,完成这个IRP的驱动把IoStatus.Status域设置为I/O操作的完成状态,而把IoStatus.Information域设置为一些要返回给调用者的额外信息。一般,在一个传输请求(读或写等会引起数据传输的请求)中IoStatus.Information 将会包含实际上被读或被写的字节数。
  • RequestorMode – 指示这个请求是从哪种模式(内核模式或用户模式)发起的。
  • Cancel, CancelIrql, and CancelRoutine – 如果正在进行时需要取消这个IRP就会用到它们。Cancel是一个BOOLEAN类型值,当被I/O管理器设置为TRUE时表示这个IRP正被取消。CancelRoutine是一个指针,在把这个IRP保存到某个队列中时由一个驱动将它设置为指向一个函数,I/O管理器会调用这个函数来让该驱动取消此IRP。因为CancelRoutine是在IRQL DISPATCH_LEVEL被调用, 而CancelIRQL是驱动应该返回到的IRQL。
  • Tail.Overlay.Thread – 指向请求线程的ETHREAD。
  • TailOverlay.ListEntry – 可能被驱动用来排队的位置,尽管它拥有这个IRP。

I/O栈位置的结构定义见NTDDK.H中的IO_STACK_LOCATION。要获得当前IRP中本驱动的I/O栈位置,驱动得调用函数IoGetCurrentIrpStackLocation(…)。

当I/O管理器开始分配IRP并初始化IRP的固定部分时,它也初始化IRP中的首个I/O栈位置。这个I/O栈位置中的信息与被传给将会处理这个请求的驱动栈中的首个驱动的信息是一致的。I/O栈位置中有以下域:

  • MajorFunction – 关联这个请求的主I/O函数代码。它从总体上指明了要执行的I/O操作的类型。
  • MinorFunction – 关联这个请求的辅助I/O函数代码。当被使用时,它可以进一步帮助程序员理解I/O请求的目的。大多数设备驱动会忽略它们。
  • Flags – 指定给正被执行的I/O函数的处理标记。
  • Control – 一组指示I/O管理器如何处理这个IRP的标记,由I/O管理器设置并参阅。例如, 若设置了SL_PENDING位(因驱动调用了IoMarkIrpPending(…))就指示给I/O管理器此IRP还需其他处理。类推之, SL_INVOKE_ON_CANCEL, SL_INVOKE_ON_ERROR和SL_INVOKE_ON_SUCCESS都指示了驱动的I/O完成例程什么时候应该被调用。
  • Parameters – 这个域由几个子成员组成,每一个子成员都是基于MajorFunction被指定。
  • DeviceObject – 这个I/O请求的目标设备对象。
  • FileObject – 关联这个请求的文件对象。

在IRP的固定部分和首个I/O栈位置之后的部分都被适当地初始化,I/O管理器会在其内部对应此MajorFunction的分发入口点中调用驱动栈顶部的驱动。因此,如果I/O管理器刚好已经构造了一个IRP来描述一个读请求,那么它会在它的读分发入口点中调用首个驱动。

也就是说,你的Windows驱动写好了,绑定到目标卷上了,何时被调用是I/O管理器说了算的。它可是驱动的“指挥官”,IRP就是它的Message“电报”。

版权声明:
作者:驱动外包
链接:http://www.51qudong.net/26.html
来源:算法优化_驱动外包_直播算法优化_MAC驱动开发_Linux驱动开发_usb驱动移植外包
文章版权归作者所有,未经允许请勿转载。

THE END
分享
二维码
< <上一篇
下一篇>>