IRP pending学习.cpp
// Complete the I/O Request
pIrp->IoStatus.Status = status;
IoCompleteRequest(pIrp, IO_NO_INCREMENT);
return status;
上面是一般驱动里面完成一个IRP后调用的代码
http://book.51cto.com/art/200912/174807.htm
IoCompleteRequest()(这个函数对应内核是 iopfCompleteRequest,而APC调用那个叫 iopCompleteRequest,注意,没f)
{
一。 //for 循环调用IRP的完成函数
for (stackPointer = IoGetCurrentIrpStackLocation( Irp ),
二。
循环unlock(unmap)这些 Irp->MdlAddress;,注意,只是unmap,没用iofreemdl
if (Irp->MdlAddress != NULL) {
//
// Unlock any pages that may be described by MDLs.
//
mdl = Irp->MdlAddress;
while (mdl != NULL) {
MmUnlockPages( mdl );
mdl = mdl->Next;
}
}
三。
//
// Make a final check here to determine whether or not this is a
// synchronous I/O operation that is being completed in the context
// of the original requestor. If so, then an optimal path through
// I/O completion can be taken.
//
如果IRP的Flags字段中的IRP_DEFER_IO_COMPLETION标志位为1,并且当前的操作是同步的,即上层的主功能函数尚未返回,那么IofCompleteRequest()到这里就返回了。我们回顾一下,NtReadFile()和NtWriteFile()都在调用IopPerformSynchronousRequest()前把这个标志位设成1
//而且deviceiocontrol这些都没有这个标志
if (Irp->Flags & IRP_DEFER_IO_COMPLETION && !Irp->PendingReturned) {
//这的理解就是,这个IRP可以异步(延迟)完成,但现在却是同步就完成了
//所以在我们自己写的驱动申请一个IRP后,如果设置了这个位IRP_DEFER_IO_COMPLETION
//并且iocalldriver()这个IRP后,如果返回的结果不是 pending 的话,是可以在iocalldriver()后面就接着使用这个IRP了的
//下面代码IopSynchronousServiceTail有这个演示,注意要自己调用iopCompleteRequest,IRP是在iopCompleteRequest里面释放掉的
if ((Irp->IoStatus.Status == STATUS_REPARSE ) &&
(Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT)) {
//
// For name junctions we reinstate the address of the appropriate
// buffer. It is freed in parse.c
//
Irp->Tail.Overlay.AuxiliaryBuffer = saveAuxiliaryPointer;
}
return;
}
四。
由于是 PendingReturned ,(标准定义就是,在 IopfCompleteRequest 中,如果 PendingReturned set了后,就是表明不是在caller线程了。IopfCompleteRequest有可能是DPC里面调用的,也可能是caller调用的)
所以需要插入一个apc IopCompleteRequest 到目标线程中
}
ntreadfile/和NtWriteFile都会调用这个IopSynchronousServiceTail
NTSTATUS
IopSynchronousServiceTail(
IN PDEVICE_OBJECT DeviceObject,
IN PIRP Irp,
IN PFILE_OBJECT FileObject,
IN BOOLEAN DeferredIoCompletion,
IN KPROCESSOR_MODE RequestorMode,
IN BOOLEAN SynchronousIo,
IN TRANSFER_TYPE TransferType
)
/*++
Routine Description:
This routine is invoked to complete the operation of a system service.
It queues the IRP to the thread's queue, updates the transfer count,
calls the driver, and finally synchronizes completion of the I/O.
Arguments:
DeviceObject - Device on which the I/O is to occur.
Irp - I/O Request Packet representing the I/O operation.
FileObject - File object for this open instantiation.
DeferredIoCompletion - Indicates whether deferred completion is possible.
RequestorMode - Mode in which request was made.
SynchronousIo - Indicates whether the operation is to be synchronous.
TransferType - Type of transfer being performed: read, write, or other.
Return Value:
The function value is the final status of the operation.
--*/
{
NTSTATUS status;
PAGED_CODE();
//
// Insert the packet at the head of the IRP list for the thread.
//
if (!SynchronousIo) {
IopQueueThreadIrp( Irp );
}
//
// Update the operation count statistic for the current process.
//
switch( TransferType ) {
case ReadTransfer:
IopUpdateReadOperationCount();
break;
case WriteTransfer:
IopUpdateWriteOperationCount();
break;
case OtherTransfer:
IopUpdateOtherOperationCount();
break;
}
//
// Now simply invoke the driver at its dispatch entry with the IRP.
//
status = IoCallDriver( DeviceObject, Irp );
//
// If deferred I/O completion is possible, check for pending returned
// from the driver. If the driver did not return pending, then the
// packet has not actually been completed yet, so complete it here.
//
if (DeferredIoCompletion) {
if (status != STATUS_PENDING) {
//这个情况下是DeferredIoCompletion的IRP被直接同步完成了,需要在caller中调用 IopCompleteRequest
//
// The I/O operation was completed without returning a status of
// pending. This means that at this point, the IRP has not been
// fully completed. Complete it now.
//
PKNORMAL_ROUTINE normalRoutine;
PVOID normalContext;
KIRQL irql = PASSIVE_LEVEL; // Just to shut up the compiler
ASSERT( !Irp->PendingReturned );
if (!SynchronousIo) {
KeRaiseIrql( APC_LEVEL, &irql );
}
IopCompleteRequest( &Irp->Tail.Apc,
&normalRoutine,
&normalContext,
(PVOID *) &FileObject,
&normalContext );
if (!SynchronousIo) {
KeLowerIrql( irql );
}
}
}
//
// If this operation was a synchronous I/O operation, check the return
// status to determine whether or not to wait on the file object. If
// the file object is to be waited on, wait for the operation to complete
// and obtain the final status from the file object itself.
//
if (SynchronousIo) {
//可以看出,不管用户是否要求同步还是异步读写文件,这里都是异步实现的。
//只是当用户如果是要求同步而且返回了pending的话,就等待下这个事件
if (status == STATUS_PENDING) {
status = KeWaitForSingleObject( &FileObject->Event,
Executive,
RequestorMode,
(BOOLEAN) ((FileObject->Flags & FO_ALERTABLE_IO) != 0),
(PLARGE_INTEGER) NULL );
if (status == STATUS_ALERTED || status == STATUS_USER_APC) {
//
// The wait request has ended either because the thread was alerted
// or an APC was queued to this thread, because of thread rundown or
// CTRL/C processing. In either case, try to bail out of this I/O
// request carefully so that the IRP completes before this routine
// exists so that synchronization with the file object will remain
// intact.
//
IopCancelAlertedRequest( &FileObject->Event, Irp );
}
status = FileObject->FinalStatus;
}
IopReleaseFileObjectLock( FileObject );
}
return status;
}
IopCompleteRequest()
{
This routine executes as a special kernel APC routine in the context of the thread which originally requested
the I/O operation which is now being completed.
This routine performs the following tasks:
o A check is made to determine whether the specified request ended with an error status. If so,
and the error code qualifies as one which should be reported to an error port,
then an error port is looked for in the thread/process. If one exists,
then this routine will attempt to set up an LPC to it.Otherwise,
it will attempt to set up an LPC to the system error port.
o Copy buffers.
o Free MDLs. //上面只是unmap了MDL,这里就真正的free
o Copy I/O status.
o Set event, if any and dereference if appropriate.
o Dequeue the IRP from the thread queue as pending I/O request.
o Queue APC to thread, if any.
o If no APC is to be queued, then free the packet now.
正常完成的话,执行下面的
if (irp->Overlay.AsynchronousParameters.UserApcRoutine) {
KeInitializeApc( &irp->Tail.Apc,
&thread->Tcb,
CurrentApcEnvironment,
IopUserCompletion, //这个kernel里面什么都不做,直接 iofreeirp
(PKRUNDOWN_ROUTINE) IopUserRundown,
(PKNORMAL_ROUTINE) irp->Overlay.AsynchronousParameters.UserApcRoutine, //这个USER APC会在KiDeliverxx里面再调用KeInitializeUserApc初始一个USER APC来插入,并不是直接在这里一步到位
irp->RequestorMode,
irp->Overlay.AsynchronousParameters.UserApcContext );
不是正常完成的话,函数退出之前会 iofreeirp
}

浙公网安备 33010602011771号