IRP三种操作

IRP的操作

通常状态下派遣函数调用IoCompleteRequest 结束当前IRP请求

但在一些情况下可能要挂起irp 用内核函数IoMarkIrpPending 挂起IRP并返回 STATUS_PENDING ,对于ring3级的请求会返回False 这时用GetLastError会发现错误码是ERROR_IO_PENDING 表明当前操作被挂起。为了最终要结束所有的IRP请求,我们需要保存被挂起的IRP,并在最后进行处理(IRP_MJ_CLEANUP

还有一种情况是取消IRP的请求IoSetCancelRoutinePIRP pIrp PDRIVER_CANCEL CancelRoutine);参数1 需要取消的IRP;参数2 :回调函数地址,需要同步执行的。

流程:在某个派遣函数A中挂起IRP 并且设置返回为STATUS_PENDING,同时需要设置取消例程,也就是回调函数。此回调函数需要同步执行,其可以在ring3级显示调用(CancelIo(hDevice) hDcvice为操作的句柄内核函数可以调用IoCancelIrp(IN pIrp) ),也可以不需要调用,程序关闭设备时会自动调用CancelIo;系统调用IoCancelIrp时自动会获得自旋锁,以进行同步,所以在回调函数完成时记得释放自旋锁(IoReleaseCancelSpinLock,否则会导致系统崩溃,另外此cancel自旋锁是全局自旋锁,所有驱动都会使用,所以不宜长时间占用。

代码见上两篇

 

Irp的串行处理

 

Irp的串行处理:IRP有很多时候也是需要同步处理的这个有个专门的名词就是串行化.串行化能够保证各个并行的IRP能够按照顺序执行典型情况下对某个设备的操作的假如有很多个线程同时去操作这个设备那么必须将这些操作排队然后一一进行处理如果不做串行化操作就会变得混乱.
 

 

当一个新的Irp请求来临的时候首先检查设备是否处于""状态如果处于忙状态那么将Irp插入一个队列中等待设备变为闲的时候从队列中取出一个IRP进行处理这个就是串行化了也就是StartIo. Windows内核中有专门支持这种操作的一系列结构和例程但是Windows提供的只有一个队列如果需要多个队列比如读一个队列写一个队列那么就要自己实现队列了

 

在驱动入口处指定startIo的派遣函数

 

pDriverObj->DriverStartIo = &StartIoRoutine;

在相应的派遣函数中将irp插入到系统队列中去

//将IRP设置为挂起
 IoMarkIrpPending(pIrp);

 //将IRP插入系统的队列
 IoStartPacket(pDevObj,pIrp,0,OnCancelIRP);//pDevObj 派遣函数参数 设备对象,cancelIRP 为IRP取消派遣例程

startIo与其他的派遣函数一样,只不过返回值为void的。

 

StartIo派遣函数开始时,获取cancel的自旋锁,判断 当前irp是否正在处理或者属于需要取消处理,如果是则释放自旋锁并立即返回。如果不是的话则不允许该请求调用取消例程(设置该IRP取消回调函数为NULL即可),释放自旋锁。然后完成Irp请求,最后读取队列中下一个Irp并进行下一轮StartIo,调用

 

函数IoStartNextPacket.

 

Cancel派遣例程开始时先判断当前irp是否在startIo中处理,如果是话则释放自旋锁并继续读取下个irp,调用IoStartNextPacket。否则从设备队列中将该Irp抽取出来(KeRemoveEntryDeviceQueue),调用此函数会自动获取自旋锁,所以调用完后记得释放自旋锁,最后进行cancel处理Irp请求。

 

自定义StartIO

系统的StartIO只能使用一个队列,所有的IRP处理都在这个队列中。然而在一些情况下可能需要各个类型的IRP分开处理,这就需要自定义startIo例程。
自定义StartIo需要自己建立管理队列,DDK中的KDVICE_QUEUE数据结构存储队列如下

typedef struct _KDEVICE_QUEUE {
    CSHORT Type;
    CSHORT Size;
    LIST_ENTRY DeviceListHead;
    KSPIN_LOCK Lock;

    BOOLEAN Busy;

} KDEVICE_QUEUE, *PKDEVICE_QUEUE, *PRKDEVICE_QUEUE;

队列中的每个元素用KDEVICE_QUEUE_ENTRY 数据结构表示

typedef struct _KDEVICE_QUEUE_ENTRY {
    LIST_ENTRY DeviceListEntry;
    ULONG SortKey;
    BOOLEAN Inserted;
} KDEVICE_QUEUE_ENTRY, *PKDEVICE_QUEUE_ENTRY, *PRKDEVICE_QUEUE_ENTRY;

IRP.Tail.Overlay.DeviceQueueEntry 就是我们需要保存的数据结构

其实我们可以建立Irp列表将整个IRP保存起来,在自定义StartIo中进行处理。

 

使用队列时需要初始化队列,因为是将该数据保存在设备扩展数据里了,所以在生成设备的时候将其初始化:

RtlZeroBytes(&pDevExt->device_queue,sizeof(pDevExt->device_queue));

KeInitializeDeviceQueue(&pDevExt->device_queue);

 

在相应的派遣函数中将其插入到队列中去 进行处理

if (!KeInsertDeviceQueue(&pDevExt->device_queue, &pIrp->Tail.Overlay.DeviceQueueEntry))
  MyStartIo(pDevObj,pIrp);

注意:进入队列之前要先将IRQL提升至DISPATCH_LEVEL级别

posted on 2012-04-19 21:10  xmcc  阅读(1314)  评论(0编辑  收藏  举报

导航