原创
IRP概述
2011-2-21 11:14
1841
7
7
分类:
通信
IRP概述 2008-03-31 11:57一、简述 任何内核模式程序在创建一个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()时,下层驱动程序的派遣例程被调用,在那些派遣例程里边调用IoGetCurrentIrpStackLocation()时得到的堆栈指针必须与下层驱动程序相对应,所以必须提前推进堆栈指针指向那个堆栈单元。 下面是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; }
文章评论(0条评论)
登录后参与讨论