IRP概述

IRP概述 收藏

一、简述
任何内核模式程序在创建一个 IRP 时,都同时创建了一个与之关联的 IO_STACK_LOCATION 结构数组:数组中的每个堆栈单元都对应一个将处理该 IRP 的驱动程序,另外还有一个堆栈单元供 IRP 的创建者使用。堆栈单元中包含该 IRP 的类型代码和参数信息以及完成函数的地址。 IRP CurrentLocation 为当前 IO 堆栈单元的索引, IRP Tail.Overlay.CurrentStackLocation 就是指向它的指针。 CurrentLocation 的最小值是 1 (注意:不是 0 )并且从上到下依次变小.每一层驱动程序都必须负责设置下一层IO堆栈的内容, 这样下一层驱动调用 IoGetCurrentIrpStackLocation() 时总能得到相应的数据。 Irp->StackCount 的值等于 IoAllocateIrp() 的第一个参数,这个值不包括 IRP 的建立者拥有的那个堆栈。
二、跟IO堆栈操作相关的几个宏的说明:

1 #define IoGetNextIrpStackLocation( Irp ) (/

(Irp)->Tail.Overlay.CurrentStackLocation - 1 )

上面已经说明, CurrentLocation 的值从上到下依次变小,而 CurrentStackLocation 与它相对应。现在返回比 CurrentStackLocation 1 的堆栈单元,实际上就是下一个单元,也就是上面的 Filter Driver 对应的那个堆栈单元。

注意:这个宏不会导致原来堆栈指针的变化。

 

2 #define IoSetNextIrpStackLocation( Irp ) {      /

    (Irp)->CurrentLocation--;                   /

(Irp)->Tail.Overlay.CurrentStackLocation--; }

 

这个宏把堆栈指针指向下一个堆栈单元。一般由 IoCallDriver() 在内部调用这个宏,因为调用 IoCallDriver() 时,下层驱动程序的派遣例程被调用,在那些派遣例程里边调用 IoGetCurrentStackLocation() 时得到的堆栈指针必须与下层驱动程序相对应,所以必须提前推进堆栈指针指向那个堆栈单元。

下面是 IoCallDriver() 的一个伪码:

NTSTATUS IoCallDriver(PDEVICE_OBJECT device, PIRP Irp)

{

 

IoSetNextIrpStackLocation(Irp);

 

PIO_STACK_LOCATION stack = IoGetCurrentIrpStackLocation(Irp);

 

stack->DeviceObject = device;

 

ULONG fcn = stack->MajorFunction;

 

PDRIVER_OBJECT driver = device->DriverObject;

 

return (*driver->MajorFunction[fcn])(device, Irp);

}

3 #define IoGetCurrentIrpStackLocation( Irp ) ( (Irp)->Tail.Overlay.CurrentStackLocation )

得到当前堆栈指针。

4 #define IoCopyCurrentIrpStackLocationToNext( Irp ) { /

    PIO_STACK_LOCATION irpSp; /

    PIO_STACK_LOCATION nextIrpSp; /

    irpSp = IoGetCurrentIrpStackLocation( (Irp) ); /

    nextIrpSp = IoGetNextIrpStackLocation( (Irp) ); /

    RtlCopyMemory(nextIrpSp,irpSp,FIELD_OFFSET(IO_STACK_LOCATION, CompletionRoutine)); /

nextIrpSp->Control = 0; }

这个例程把“除了完成例程以外”的其它所有 IO 堆栈单元数据拷贝到下一个堆栈单元,以备下层驱动程序调用。

5 #define IoSkipCurrentIrpStackLocation( Irp ) /

    (Irp)->CurrentLocation++; /

(Irp)->Tail.Overlay.CurrentStackLocation++;

有些时候并不需要拷贝整个堆栈单元数据到下层堆栈,而是直接引用当前堆栈。利用这个宏就可以达到这个目的。这个宏把堆栈指针推回到上一层堆栈,回想一下上面给出的 IoCallDriver() 的伪码,在里边调用了 IoSetNextIrpStackLocation() ,而那个宏把堆栈指针推向下一层堆栈,两者中和的结果就是堆栈指针不变,这样当下层驱动调用 IoGetCurrentStackLocation() 时,它得到的堆栈指针和我们得到的指针完全一样。

三、推迟IRP地完成

 

如果一个驱动不能立刻完成一个 IRP ,则它可以返回 STATUS_PENDING 状态标明它还不能完成这个 IRP ,并且以后在完成该 IRP 会调用 IoCompleteRequest() 来完成这个 IRP

Code1:

IoMarkIrpPending(Irp)

Return STATUS_SUCCESS;

Code2:

Irp->IoStatus.Status = <Final-Status>

Irp->IoStatus.Information = <Size>

IoCompleteRequest(Irp);

Return <Final->Status>

四、IRP完成例程

本驱动的完成例程是设置在下一层IO堆栈中的

 

调用

IoSetCompletionRoutine(



 

IN PIRP

 

Irp  

,



 

IN PIO_COMPLETION_ROUTINE

 

CompletionRoutine  

,



 

IN PVOID

 

Context  

,



 

IN BOOLEAN

 

InvokeOnSuccess  

,



 

IN BOOLEAN

 

InvokeOnError  

,



 

IN BOOLEAN

 

InvokeOnCancel  



     );

#define IoSetCompletionRoutine( Irp, Routine, CompletionContext, Success, Error, Cancel ) { /

    PIO_STACK_LOCATION irpSp;                                               /

    ASSERT( (Success) | (Error) | (Cancel) ? (Routine) != NULL : TRUE );    /

    irpSp = IoGetNextIrpStackLocation( (Irp) );                             /

    irpSp->CompletionRoutine = (Routine);                                   /

    irpSp->Context = (CompletionContext);                                   /

    irpSp->Control = 0;                                                      /

    if ((Success)) { irpSp->Control = SL_INVOKE_ON_SUCCESS;

}               /

    if ((Error)) { irpSp->Control |= SL_INVOKE_ON_ERROR; }                  /

    if ((Cancel)) { irpSp->Control |= SL_INVOKE_ON_CANCEL; } }

可以设置一个完成例程。实际上这是一个宏,它的任务就是设置完成例程到下层驱动程序对应的 IO 堆栈中。当下层驱动调用 IoCompleteRequest() 时,这个函数将检查下层驱动对应的堆栈里边是否已经设置了完成例程。如果设置了,则调用相应的完成例程。 IoCompleteRequest() 将重复这个过程,直到已经到达了最上层驱动或者中间某个完成例程返回了 STATUS_MORE_PROCESSING_REQUIRED 状态。

完成例程一般如下:
NTSTATUS CompletionRoutine(PDEVICE_OBJECT DeviceObject, PIRP Irp, PVOID context)

{

 

if (Irp->PendingReturned)

    

IoMarkIrpPending(Irp);

 

...

 

return <some status code>

;

}

 

其中 DeviceObject 就是设置完成例程的当前设备对象,调用 IoGetCurrentIrpStackLocation() 得到的堆栈也是当前设备对象对应的堆栈。

注意前面两行代码,任何一个不返回 STATUS_MORE_PROCESSING_REQUIRED 状态的完成例程都应该有两行代码,这是为了保证 IoCompleteRequest() 可以一直重复调用上一层驱动的完成例程。

如果一个完成例程返回 STATUS_MORE_PROCESSING_REQUIRED 状态, IoCompleteRequest() 将停止继续调用上一层驱动设置的完成例程,这时候的 IRP 处于一个中间状态,因此相应驱动的派遣例程有责任继续完成这个 IRP ,如下所示:

NTSTATUS

SfDirectoryControl(

    IN PDEVICE_OBJECT DeviceObject,

    IN PIRP Irp

    )

{

    PIO_STACK_LOCATION        IrpStack;

    IrpStack = IoGetCurrentIrpStackLocation(Irp);

    //

    // DO something at here

    //       

    Irp->IoStatus.Status = Status;

    IoCompleteRequest(Irp, IO_NO_INCREMENT);

    return Status;

}

 

//------------------------------------------- --------------------------------

//

// 处理 IRP_MJ_DIRECTORY_CONTROL 的完成例程

//

NTSTATUS

SfDirectoryControlCompletion (

    IN PDEVICE_OBJECT    DeviceObject ,

    IN PIRP              Irp ,

    IN PVOID             Context

    )

{                               

    UNREFERENCED_PARAMETER ( DeviceObject );

    UNREFERENCED_PARAMETER ( Irp );

//

// 不能再调用 IoMarkIrpPending() 函数,因为返回值是

// STATUS_MORE_PROCESSING_REQUIRED

//

    KeSetEvent (( PKEVENT ) Context , IO_NO_INCREMENT , false );

   

    return STATUS_MORE_PROCESSING_REQUIRED ;

}

转自:  http://hi.baidu.com/justin_wu2010/blog/item/2e726f0355bb44733812bb7a.html

你可能感兴趣的:(r)