不同的方法来处理 Irp 的作弊工作表 (第 1 部分,共 2 部分)

本文详细探讨了Windows驱动程序模型中如何通过Irp转发数据至其他驱动程序,包括不同转发策略及其实现代码示例。重点介绍了IRP完成例程的使用规则和优化方法,帮助开发者更高效地处理Irp。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

简介

Windows 驱动程序模型 (WDM) 驱动程序中最常用完成的任务之一将输入/输出请求数据包 (Irp) 从一个驱动程序发送到另一个驱动程序中。 驱动程序将创建其自己的 IRP,并将其发送到一个较低的驱动程序,或者驱动程序会将它从上面附加的另一个驱动程序接收 Irp 转发。

本文讨论了驱动程序可以发送 Irp 到带批注的示例代码使用较低的驱动程序的所有可能的方式。 根据需要,驱动程序编写人员可以按照本文中提供的模板之一,而且不会受旧 IRP 处理规则。

显示有关如何将 IRP 转发给另一个驱动程序,从一个调度例程 5 方案,这一主题的第 1 部分,并 (在此主题中的第 2 部分中列出) 的剩余 7 方案讨论不同的方法来创建 IRP,并将其发送到另一个驱动程序。 这一主题的第二部分包含在以下知识库文章:
326315 不同的方法来处理 Irp 的便笺 (2 的第 2 部分)
检查各种方案之前,请注意下列有关完成例程将返回的状态:
IRP 完成例程可以返回 STATUS_MORE_PROCESSING_REQUIRED 或 STATUS_SUCCESS。
它会检查状态时,I/O 管理器使用下面的规则:
  • 如果状态是 STATUS_MORE_PROCESSING_REQUIRED,完成 IRP,停止将保留不变的堆栈位置并返回。
  • 如果状态是 STATUS_MORE_PROCESSING_REQUIRED 以外的任何值,继续完成 IRP 向上。
由于 I/O 管理器不需要知道使用哪些非 STATUS_MORE_PROCESSING_REQUIRED 值,使用 STATUS_SUCCESS (因为在大多数处理器体系结构上是可有效地加载值 0)。

以提高代码、 Windows XP SP1 和 Windows XP.NET 驱动程序开发工具包 Ntddk.h 和 Wdm.h 头文件的可读性将具有新#define名为 STATUS_CONTINUE_COMPLETION,这是化名为 STATUS_SUCCESS,如下面的代码所示:
// 
// This value should be returned from completion routines to continue
// completing the IRP upwards. Otherwise, STATUS_MORE_PROCESSING_REQUIRED
// should be returned.
// 
#define STATUS_CONTINUE_COMPLETION      STATUS_SUCCESS
// 
// Completion routines can also use this enumeration instead of status codes.
// 
typedef enum _IO_COMPLETION_ROUTINE_RESULT {
    
    ContinueCompletion = STATUS_CONTINUE_COMPLETION,
    StopCompletion = STATUS_MORE_PROCESSING_REQUIRED

} IO_COMPLETION_ROUTINE_RESULT, *PIO_COMPLETION_ROUTINE_RESULT;
				

更多信息

方案 1: 转发不用再管

如果驱动程序只是想要转发 IRP 向下,不执行任何其他操作,请使用下面的代码。 该驱动程序没有在这种情况下设置完成例程。 如果该驱动程序是一个最高级别的驱动程序,IRP 可以完成同步或异步方式,根据较低的驱动程序返回的状态。
NTSTATUS
DispatchRoutine_1(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    // 
    // You are not setting a completion routine, so just skip the stack
    // location because it provides better performance.
    // 
    IoSkipCurrentIrpStackLocation (Irp);
    return IoCallDriver(TopOfDeviceStack, Irp);
} 

方案 2: 前进和等待

如果要转发 IRP 到较低的驱动程序,并等待其返回,以便它可以处理 IRP 驱动程序,请使用下面的代码。 这样做通常是处理即插即用的 Irp 时。 例如,在收到 IRP_MN_START_DEVICE IRP 时,必须转发 IRP 到总线驱动程序,并等待其完成,然后才能启动您的设备。 Windows XP 系统都有一个名为IoForwardIrpSynchronously ,您可以使用轻松地执行此操作的新函数。
NTSTATUS
DispatchRoutine_2(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    KEVENT   event;
    NTSTATUS status;

    KeInitializeEvent(&event, NotificationEvent, FALSE);

    // 
    // You are setting completion routine, so you must copy
    // current stack location to the next. You cannot skip a location
    // here.
    // 
    IoCopyCurrentIrpStackLocationToNext(Irp);

    IoSetCompletionRoutine(Irp,
                           CompletionRoutine_2,
                           &event,
                           TRUE,
                           TRUE,
                           TRUE
                           );

    status = IoCallDriver(TopOfDeviceStack, Irp);

    if (status == STATUS_PENDING) {
        
       KeWaitForSingleObject(&event,
                             Executive, // WaitReason
                             KernelMode, // must be Kernelmode to prevent the stack getting paged out
                             FALSE,
                             NULL // indefinite wait
                             );
       status = Irp->IoStatus.Status;
    }
    
    // <---- Do your own work here.


    // 
    // Because you stopped the completion of the IRP in the CompletionRoutine
    // by returning STATUS_MORE_PROCESSING_REQUIRED, you must call
    // IoCompleteRequest here.
    // 
    IoCompleteRequest (Irp, IO_NO_INCREMENT);
    return status;

}
NTSTATUS
CompletionRoutine_2(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{ 
  if (Irp->PendingReturned == TRUE) {
    // 
    // You will set the event only if the lower driver has returned
    // STATUS_PENDING earlier. This optimization removes the need to
    // call KeSetEvent unnecessarily and improves performance because the
    // system does not have to acquire an internal lock.  
    // 
    KeSetEvent ((PKEVENT) Context, IO_NO_INCREMENT, FALSE);
  }
  // This is the only status you can return. 
  return STATUS_MORE_PROCESSING_REQUIRED;  
} 
				

方案 3: 转发与完成例程

在这种情况下,驱动程序设置完成例程、 转发 IRP 向下,然后返回是较低的驱动程序的状态。 设置完成例程的目的是在其返回途中 IRP 的内容进行修改。
NTSTATUS
DispathRoutine_3(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    NTSTATUS status;

    // 
    // Because you are setting completion routine, you must copy the
    // current stack location to the next. You cannot skip a location
    // here.
    // 
    IoCopyCurrentIrpStackLocationToNext(Irp); 

    IoSetCompletionRoutine(Irp,
                           CompletionRoutine_31,// or CompletionRoutine_32
                           NULL,
                           TRUE,
                           TRUE,
                           TRUE
                           );
    
    return IoCallDriver(TopOfDeviceStack, Irp);

} 
如果您从您的调度例程返回较低的驱动程序的状态:
  • 您不得更改完成例程中 IRP 的状态。这是为了确保在 IRP 的 IoStatus 块 (Irp-> IoStatus.Status) 中设置的状态值都是相同的低级驱动程序的返回状态。
  • 必须将等待状态的 IRP 的传播由 Irp-> PendingReturned。
  • 您不得更改完成 IRP 的这项工作。
因此,仅有 2 个在此方案中 (31 和 32) 完成例程的有效版本:
 
NTSTATUS
CompletionRoutine_31 (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{   

    // 
    // Because the dispatch routine is returning the status of lower driver
    // as is, you must do the following:
    // 
    if (Irp->PendingReturned) {
        
        IoMarkIrpPending( Irp );
    }
    
    return STATUS_CONTINUE_COMPLETION ; // Make sure of same synchronicity 
}

NTSTATUS
CompletionRoutine_32 (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{   
    // 
    // Because the dispatch routine is returning the status of lower driver
    // as is, you must do the following:
    // 
    if (Irp->PendingReturned) {
        
        IoMarkIrpPending( Irp );
    }
    
    //    
    // To make sure of the same synchronicity, complete the IRP here.
    // You cannot complete the IRP later in another thread because the 
    // the dispatch routine is returning the status returned by the lower
    // driver as is.
    // 
    IoCompleteRequest( Irp,  IO_NO_INCREMENT);  

    // 
    // Although this is an unusual completion routine that you rarely see,
    // it is discussed here to address all possible ways to handle IRPs.  
    // 
    return STATUS_MORE_PROCESSING_REQUIRED; 
} 

				

方案 4: 为更高版本,进行排队或转发和重新使用

在驱动程序要排队 IRP 以及以后对其进行处理,或正向 IRP 到较低的驱动程序和一定数量的时间之前完成 IRP 时重用它的情况下,使用下面的代码段。 调度例程标记挂起 IRP,并返回 STATUS_PENDING,因为打算以后在另一个线程完成 IRP。 在这里,完成例程可以更改 IRP,如有必要 (与以前的方案) 的状态。
NTSTATUS
DispathRoutine_4(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    NTSTATUS status;

    // 
    // You mark the IRP pending if you are intending to queue the IRP
    // and process it later. If you are intending to forward the IRP 
    // directly, use one of the methods discussed earlier in this article.
    // 
    IoMarkIrpPending( Irp );    

    // 
    // For demonstration purposes: this IRP is forwarded to the lower driver.
    // 
    IoCopyCurrentIrpStackLocationToNext(Irp); 

    IoSetCompletionRoutine(Irp,
                           CompletionRoutine_41, // or CompletionRoutine_42
                           NULL,
                           TRUE,
                           TRUE,
                           TRUE
                           ); 
    IoCallDriver(TopOfDeviceStack, Irp);

    // 
    // Because you marked the IRP pending, you must return pending,
    // regardless of the status of returned by IoCallDriver.
    // 
    return STATUS_PENDING ;

}
STATUS_CONTINUE_COMPLETION 或 STATUS_MORE_PROCESSING_REQUIRED,则既可以返回完成例程。 仅当您想重新使用另一个线程从 IRP,以后再完成它,您可以返回 STATUS_MORE_PROCESSING_REQUIRED。
NTSTATUS
CompletionRoutine_41(
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{ 
    // 
    // By returning STATUS_CONTINUE_COMPLETION , you are relinquishing the 
    // ownership of the IRP. You cannot touch the IRP after this.
    // 
    return STATUS_CONTINUE_COMPLETION ; 
} 


NTSTATUS
CompletionRoutine_42 (
    IN PDEVICE_OBJECT   DeviceObject,
    IN PIRP             Irp,
    IN PVOID            Context
    )
{  
    // 
    // Because you are stopping the completion of the IRP by returning the
    // following status, you must complete the IRP later.
    // 
    return STATUS_MORE_PROCESSING_REQUIRED ; 
} 
				

方案 5: 完成 IRP 中调度例程

此方案演示如何完成 IRP 调度例程中。

重要 调度例程的返回状态完成 IRP 中调度例程时,应与 IRP 的 IoStatus 块中设置的值的状态相匹配 (Irp-> IoStatus.Status)。
NTSTATUS
DispatchRoutine_5(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    // 
    // <-- Process the IRP here.
    // 
    Irp->IoStatus.Status = STATUS_XXX;
    Irp->IoStatus.Information = YYY;
    IoCompletRequest(Irp, IO_NO_INCREMENT);
    return STATUS_XXX;
} 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值