21
1月
2016

6、深入理解IRP的完成机制

    Windows 驱动程序模型 (WDM) 驱动程序中最常完成任务之一是从一个驱动程序发送输入/输出请求数据包 (Irp)向另一个驱动程序。驱动程序将创建其自己的 IRP 并将其发送到一个较低的驱动程序,或者驱动程序将它从上面附加另一个驱动程序接收 Irp 转发。 本文讨论所有可能的方式,驱动程序可以发送 Irp 到较低的驱动程序也可以根据驱动程序编写人员需要按照示例代码提供的模板之一进行处理。

IRP 完成例程可以返回 STATUS_MORE_PROCESSING_REQUIRED STATUS_SUCCESS。它检查状态时,I/O 管理器使用下列规则:•如果状态是 STATUS_MORE_PROCESSING_REQUIRED,停止完成 IRP,离开的堆栈位置保持不变,并返回。 •如果状态不是 STATUS_MORE_PROCESSING_REQUIRED,继续完成 IRP 向上。

// 
// 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 。(在 PDEVICE_OBJECT DeviceObject,IN PIRP 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 的限。 因此,仅有这种情况下 (31 和 32) 中完成例程的 2 个有效版本:

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 的调度例程返回的状态应与 IoStatus 块的 IRP (Irp-> IoStatus.Status) 中设置的值的状态相匹配。

NTSTATUS
DispatchRoutine_5(
    IN PDEVICE_OBJECT DeviceObject,
    IN PIRP Irp
    )
{
    // 
    // IoStatus.Status = STATUS_XXX;
    Irp->IoStatus.Information = YYY;
    IoCompletRequest(Irp, IO_NO_INCREMENT);
    return STATUS_XXX;
} 

Categories: 驱动开发

Overall Rating (0)

0 out of 5 stars

Leave your comments

Post comment as a guest

0 Character restriction
Your text should be more than 3 characters
  • No comments found