在中间级驱动程序中处理 IRP
与最低级别的设备驱动程序相比,较高级别的驱动程序具有一组不同的标准例程,两种类型的驱动程序通用的标准例程子集重叠。
中级和最高级别驱动程序的例程集也因以下条件而异:
基础物理设备的性质
基础设备驱动程序是否为直接 I/O 或缓冲 I/O 设置设备对象
单个更高级别的驱动程序的设计
下图演示了 IRP 可能通过中间镜像驱动程序的标准例程采用的路径,这些例程在上一部分所述的最低级别设备驱动程序上分层。
下图中显示的驱动程序具有以下特征:
驱动程序在多个物理设备上分层,可能通过多个设备驱动程序进行分层。
驱动程序有时会为较低级别的驱动程序分配其他 IRP,具体取决于输入 IRP 中请求的操作。
该驱动程序的上方至少具有一个分层的文件系统驱动程序,并且该文件系统驱动程序可能分层到比此更高级别的其他中间驱动程序上。
如图所示,I/O 管理器创建 IRP 并将其发送到给定主要函数代码的驱动程序调度例程。 假设函数 代码IRP_MJ_WRITE,则调度例程为 DDDispatchWrite。 中间驱动程序的 I/O 堆栈位置显示在中间,更高级别和较低级别驱动程序的 I/O 堆栈位置不限数量显示为阴影。
分配 IRP
镜像驱动程序的目的是向多个物理设备发送写入请求,并交替向这些设备的驱动程序发送读取请求。 对于写入请求,驱动程序将为要写入数据的每个设备创建重复的 IRP,前提是输入 IRP 中的参数有效。
上图显示了对 IoAllocateIrp 的调用,但更高级别的驱动程序可以调用其他支持例程来为较低级别的驱动程序分配 IRP。 请参阅 为Lower-Level驱动程序创建 IRP。
当调度例程调用 IoAllocateIrp 时,它指定 IRP 所需的 I/O 堆栈位置数。 驱动程序必须为链中的每个较低级驱动程序指定堆栈位置,从每个驱动程序的设备对象中获取适当的值,这些对象位于镜像驱动程序的正下方。 (可选)驱动程序可以在调用 IoAllocateIrp 以获取其分配的每个 IRP 的堆栈位置时向此值添加一个,就像上图中的驱动程序一样。
此中间驱动程序的调度例程调用 IoGetCurrentIrpStackLocation (原始 IRP 未显示) ,以检查参数。
它调用 IoSetNextIrpStackLocation ,因为它在每个新创建的 IRP 和 IoGetCurrentIrpStackLocation 中分配了自己的堆栈位置,以便为其自己创建一个上下文,稍后在 IoCompletion 例程中使用。
接下来,它会使用每个新创建的 IRP 调用 IoGetNextIrpStackLocation ,以便它可以在分配的 IRP 中设置下一个较低级别的驱动程序的 I/O 堆栈位置。 镜像驱动程序的调度例程将 IRP 函数代码和参数 (指向传输缓冲区的指针、要传输IRP_MJ_WRITE) 的字节长度复制到下一个较低驱动程序的 I/O 堆栈位置。 反过来,这些驱动程序会为它们正下方的驱动程序设置 I/O 堆栈位置(如果有)。
调用 IoSetCompletionRoutine 和 IoCallDriver
上图中的调度例程为其分配的每个 IRP 调用 IoSetCompletionRoutine 。 由于上图中的驱动程序必须释放其分配的 IRP,因此此驱动程序会将 IoCompletion 例程设置为在较低级驱动程序完成其 IRP 时调用,无论 I/O 操作是成功完成、失败还是已取消。
由于上图中的驱动程序并行镜像,因此它通过调用 IoCallDriver 两次(针对表示镜像分区的每个目标设备对象调用 IoCallDriver )将分配给下一级别驱动程序的两个 IRP。
在驱动程序的 IoCompletion 例程中处理 IRP
当任一组较低级别的驱动程序完成请求的操作时,I/O 管理器将调用中间镜像驱动程序的 IoCompletion 例程。 镜像驱动程序在原始 IRP 的自己的 I/O 堆栈位置维护计数,以跟踪低级驱动程序完成所有重复 IRP 的时间。
假设 I/O 状态块指示一组较低版本的驱动程序已完成上图中显示的重复 IRP,镜像驱动程序的 IoCompletion 例程会递减其计数,但在将计数减为零之前无法完成原始 IRP。 如果递减的计数尚未为零, IoCompletion 例程使用上图中第一个返回的 IRP (DupIRP1 调用 IoFreeIrp ,) 驱动程序分配并返回STATUS_MORE_PROCESSING_REQUIRED。
使用上图所示的 DupIRP2 再次调用镜像驱动程序的 IoCompletion 例程时,IoCompletion 例程会递减原始 IRP 中的计数,并确定两组较低级别的驱动程序都执行了请求的操作。
假设 DupIRP2 中的 I/O 状态块也是使用 STATUS_SUCCESS 设置的, IoCompletion 例程会将 I/O 状态块从 DupIRP2 复制到原始 IRP 并释放 DupIRP2。 它使用原始 IRP 调用 IoCompleteRequest ,并返回STATUS_MORE_PROCESSING_REQUIRED。 返回此状态会阻止 I/O 管理器尝试对 DupIRP2 进行任何进一步的完成处理;由于 IRP 不与线程关联,因此其完成处理应以创建线程的驱动程序结束。
如果任一组较低级别的驱动程序未成功完成镜像驱动程序的 IRP,镜像驱动程序的 IoCompletion 例程应记录错误并尝试适当的镜像数据恢复。 有关详细信息,请参阅 日志记录错误。