现在的位置: 首页 > 综合 > 正文

IRP 完成例程

2013年10月06日 ⁄ 综合 ⁄ 共 4421字 ⁄ 字号 评论关闭
  

1.最高层驱动总是运行在发起该请求的程序所处的线程中。DriverEntry总是处在系统线程中,系统线程的空间不涉及到线性地址 0 - 2G。APC 是处在任意上下文中,它所在的线程取决于系统运行APC之前最后被挂起的那个线程。
2. 所有不返回 STATUS_MORE_PROCESSING_REQUIRED的完成回调例程,需要使用下面的代码:
NTSTATUS MyCompletionRoutine(PDEVICE_OBJECT DevObj, PIRP Irp, VOID Context)
{
 if (Irp->PendingRetured)
 {
  IoMarkIrpPending(Irp);
 }
 < your processing code ... >
 return STATUS_SUCCESS;
}
原因在于IRP的发起者通常希望同等的等待该IRP的完成,所以在构造IRP的时候有以下的逻辑:
////////////////////////////////////////////////////////////
KEVENT     event;
IO_STATUS_BLOCK  iosb;
KeInitializeEvent(&event, ....);
PIRP  Irp = IoBuildDeviceIoControlRequest(..., &event, &iosb);
NTSTATUS    status = IoCallDriver(someDeviceObject, Irp);
if (status == STATUS_PENDING)
{
 KeWaitForSingleObject(&event, ...);
 status = iosb.Status;
}
/////////////////////////////////////////////////////////
宏: IoMarkIrpPending(Irp)  (/
IoGetCurrentIrpStackLocation((Irp))->Control |= SL_PENDING_RETURND)
这儿会等待一个事件,该事件是在完成例程派遣的一个APC里面设置的。派遣该APC的一个测试条件是:最高层栈有一个SL_PENDING_RETURED的标志,其实就是 Irp->Control 这个域。在底层,某个驱动调用 IoMarkIrpPending(Irp),然后返回了 STATUS_PENDING, 那么就会出现上层在等待一个事件的情况。某一时刻,IoCompleteIrp会被调用,它会把Irp->PendingReturned 的值设置为 PIO_STACK_LOCATION->Control 中的值。所有依次被调用的完成回调例都要这么做,才能把该SL_PENDING_RETURED标志传到最上层的设备栈,然后IO管理才派遣一个APC来做这个事情。 使用APC的原因在于,可以使得IoCompleteRequest的调用可以发生在任意上下文中。而IoCompleteRequest必须能发生在任意上下文中,因为对于费时的操作,StartIo会启动设备,然后等待设备完成传输后的中断,在中断中,调用ISR,ISR调用DPC,这时候必然处在任意上下文中,而我们知道,通常会在DPC中完成IRP,所以要求IoCompleteRequest 发生在任意上下文中。
 要注意的是, 对于某些IRP,如果上层设备栈的 SL_PENDING_RETURNED 被置位,那么IoCompleterRequest会试图派遣一个APC去完成IRP。但是,如果IRP的发起者通过IoCallDriver得到的不是STATUS_PENDING 的状态,那么在IO管理器会在构造IRP的地方就把它清理掉。所以,返回STATUS_PENDING 和 IoMarkIrpPending 要同时调用,不要只调用它们中的一个。
 以下四种试图避开该调用的操作是不可取的。
Ø 在Dispatch 例程中有条件的调用IoMarkIrpPending
NTSTATUS TopDriverDispatchSomething(PDEVICE_OBJECT fidp, PIRP irp)
{
 PDEVICE_EXTENSION pdx = 
 (PDEVICE_EXTENSION)fido->DevcieExtension;
 IoCopyCurrentIrpStackLocationToNext(irp);
 IoSetCompletionRoutine(irp, TopDriverCompletionRoutine, ...);
 NTSTATUS status = IoCallDriver(pdx->LowerDevice, irp);
 if (STATUS_PENDING == status)
 {
  IoMarkIrpPending(Irp); // 此时, IRP,有可能已经完成了,这里会破坏内存;
 }      // 在驱动中,调用了IoCallDriver后,就不要碰 IRP 了
 return status;
}
Ø 总在 Dispatch 例程中调用 IoMarkIrpPending(...)
NTSTATUS TopDrvierDispatchSomething(PDEVICE_OBJECT fido, PIRP irp)
{
 PDEVICE_EXTENSION pdx = fido->DeviceExtension;
 IoMarkIrpPending(irp);
 IoCopyCurrrentIrpStackLocationToNext(irp);
 IoSetCompletionRoutine(irp, TopDriverCompleteRoutine, ...);
 return IoCallDriver(pdx->LowrDevice, irp);
}
这里的问题在于,如果底层驱动返回的不是 STATUS_PENDING, 因为调用了IoMarkIrpPending ,所以IoCompleteRequest 会派遣APC完成该IRP,但是由于返回的不是STATUS_PENDING, I/O 管理器在构造IRP的地方也可能完成它,所以会出现两次完成的情况。 
Ø 在完成回调例程中总是调用 IoMarkIrpPending 
NTSTATUS TopDevierDeispatchSomething(PDEVCIE_OBJECT fido, PIRP irp)
{
 PDEVICE_EXTENSION pdx = fido->DeviceExtenson;
 KEVENT     event;
 KeInitializeEvent(&event, NotificaionEvent, FALSE);
 IoCopyCurrentIrpStackLocationToNext(irp);
 IoSetCompletionRoutine(irp, TopDriverCompletionRoutine, &event, TRUE,TRUE,TRUE);
 IoCallDriver(pdx->LowerDeviceObject, irp);
 KeWaitForSingleObject(&event, ...);
 Irp->IoStatus.Status = status;
 IoCompleteRequest(irp, IO_NO_INCREMENT);
 return status;
}
NTSTATUS TopDriverCompletionRoutine(PDEVICE_OBJECT fido, PIRP irp, PVOID pev)
{
 if (Irp->PendingReturned)
{
 IoMarkIrpPending(irp);
}
KeSetEvent((PKEVENT)pev, IO_NO_CREMENT, FALSE);
return STATUS_MORE_PROCESSING_REQUIRED;
}
这里意图将IRP同步的向下层驱动传递,等到下层驱动处理完毕后,我们再处理它。但是这里的问题在于,如果下层驱动返回了STATUS_PENDING , 那么会造成在IRP栈中SL_PENDING_RETUREND 置位,并且dispatch 例程不返回 STATUS_PENDING 的状态,会发生同上面一样的错误。
下面的code也会有问题, 一个驱动构造了一个异步的IRP以帮助自己完成某些工作,然后提供了一个完成例程以释放这个IRP.
SOMETYPE SomeFunction()
{
 PIRP irp = IoBuildAsynchronousFsdRequest(...);
 IoSetCompleteRoutine(irp, MyCompleteRoutine, ...);
 IoCallDriver(...);
}
NTSTATUS MyCompletionRoutine(PDEVCIE_OBJECT junk, PIRP irp, PVOID context)
{
 if (Irp->PendingReturned)
 {
  IoMarkIrpPending(irp);
 }
 IoFreeIrp(irp);
 return STATUS_MORE_PROCESISING_REQUIRED;
}
这里的问题在于,由于这里已经没有与之对应的IRP栈,所以IoMarkIrpPending设置的标志会破坏别的内存。永远记住,在返回STATUS_MORE_PROCESSING_REQUIRED的完成回调例程中,不要调用 IoMarkIrpPending 就好了。
Ø 总是返回 STATUS_PENDING.
NTSTATUS TopDriverDispatchSomething(PDEVCIE_OBJECT fido, PIRP irp)
{
 PDEVICE_EXTENSION pdx = fido->DeviceExtension;
 IoMarkIrpPending(irp);
 IoCopyCurrentIrpStackLocationToNext(irp);
 IoSetCompleteionRoutine(irp, TopDriverCompletionRoutine, ...);
 return STATUS_PENDING
}
NTSTATUS TopDriverCompletionRoutine(PDEVICE_OBJECT fido, PIRP irp)
{
 ....
 return STATUS_SUCCESS;
}
这里没有什么大问题,但是效率不高。
  

抱歉!评论已关闭.