本文主要是介绍《Windows NT FileSystem Internals》学习笔记之Complete IRP,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
原来以为完成IRP的处理仅仅是简单的调用一下IoCompleteRequest()函数而已,内部的代码应该很简单。阅读了Win2K源代码才发现,事情并不简单啊,呵呵,再一次发现Win2K源代码的价值了。
驱动必须调用IoCompleteRequest()通知I/O管理器相应的IRP已经处理完毕。IoCompleteRequest函数的处理过程如下(对应着Win2K源代码阅读,在ioStubs.c中):
IoCompleteRequest调用过程如下:
IoCompleteRequestàIofCompleteRequestàIopfCompleteRequest(不知道微软的函数调用经过这么多过程干吗)àIopCompleteRequest(由APC执行)
1. I/O管理器执行一些基本检查确保IRP在正确的状态。当前StackLocation的值必须小于等于StacksCount+1。如果该值不正确,系统返回错误码为MULTIPLE_IRP_COMPLETE_REQUEST的bugcheck。如果安装的是debug build版本的操作系统,I/O管理器将执行一些附加的Assertion,例如检查STATUS_PENDING的返回状态码,以及其他从驱动返回值,
//
// Begin by ensuring that this packet has not already been completed
// by someone.
//
if (Irp->CurrentLocation > (CCHAR) (Irp->StackCount + 1) ||
Irp->Type != IO_TYPE_IRP) {
KeBugCheckEx( MULTIPLE_IRP_COMPLETE_REQUESTS, (ULONG_PTR) Irp, __LINE__, 0, 0 );
}
//
// Ensure that the packet being completed really is still an IRP.
//
ASSERT( Irp->Type == IO_TYPE_IRP );
//
// Ensure that no one believes that this request is still in a cancelable
// state.
//
ASSERT( !Irp->CancelRoutine );
//
// Ensure that the packet is not being completed with a thoroughly
// confusing status code. Actually completing a packet with a pending
// status probably means that someone forgot to set the real status in
// the packet.
//
ASSERT( Irp->IoStatus.Status != STATUS_PENDING );
//
// Ensure that the packet is not being completed with a minus one. This
// is apparently a common problem in some drivers, and has no meaning
// as a status code.
//
ASSERT( Irp->IoStatus.Status != 0xffffffff );
//
// Ensure that if this is a paging I/O operation, and it failed, that the
// reason for the failure isn't because quota was exceeded.
//
ASSERT( !(Irp->Flags & IRP_PAGING_IO && Irp->IoStatus.Status == STATUS_QUOTA_EXCEEDED ) );
2. I/O管理器开始扫描IRP包含的所有需要调用的Completion Routine。每一个Stack Location都可以包含一个Completion Routine,这些Completion Routine可以在IRP处理返回成功、错误或者取消时调用。I/O Stack Location采用反向顺序扫描,最高StackLocation最先扫描。这样磁盘驱动(最底层)提供的Completion Routine将最先调用,而最高层驱动的Completion Routine最后调用。
Completion Routine在调用IoCompleteRequest()的线程上下文中调用,如果某个Completion Routine返回STATUS_MORE_PROCESSION_REQUIRED,I/O管理器停止IRP事后处理,将程序控制权返回调用IoCompleteRequeset()的程序。返回STATUS_MORE_PROCESSING_REQUIRED程序负责调用FreeIrp()。
//
// Now check to see whether this is the last driver that needs to be
// invoked for this packet. If not, then bump the stack and check to
// see whether the driver wishes to see the completion. As each stack
// location is examined, invoke any routine which needs to be invoked.
// If the routine returns STATUS_MORE_PROCESSING_REQUIRED, then stop the
// processing of this packet.
//
for (stackPointer = IoGetCurrentIrpStackLocation( Irp ),
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++;
Irp->CurrentLocation <= (CCHAR) (Irp->StackCount + 1);
stackPointer++,
Irp->CurrentLocation++,
Irp->Tail.Overlay.CurrentStackLocation++) {
//
// A stack location was located. Check to see whether or not it
// has a completion routine and if so, whether or not it should be
// invoked.
//
// Begin by saving the pending returned flag in the current stack
// location in the fixed part of the IRP.
//
Irp->PendingReturned = stackPointer->Control & SL_PENDING_RETURNED;
if ( (NT_SUCCESS( Irp->IoStatus.Status ) &&
stackPointer->Control & SL_INVOKE_ON_SUCCESS) ||
(!NT_SUCCESS( Irp->IoStatus.Status ) &&
stackPointer->Control & SL_INVOKE_ON_ERROR) ||
(Irp->Cancel &&
stackPointer->Control & SL_INVOKE_ON_CANCEL)
) {
//
// This driver has specified a completion routine. Invoke the
// routine passing it a pointer to its device object and the
// IRP that is being completed.
//
ZeroIrpStackLocation( stackPointer );
PERFINFO_DRIVER_COMPLETIONROUTINE_CALL(Irp, stackPointer);
status = stackPointer->CompletionRoutine( (PDEVICE_OBJECT) (Irp->CurrentLocation == (CCHAR) (Irp->StackCount + 1) ? (PDEVICE_OBJECT) NULL :IoGetCurrentIrpStackLocation( Irp )->DeviceObject), Irp,stackPointer->Context );
PERFINFO_DRIVER_COMPLETIONROUTINE_RETURN(Irp, stackPointer);
if (status == STATUS_MORE_PROCESSING_REQUIRED) {
//
// Note: Notice that if the driver has returned the above
// status value, it may have already DEALLOCATED the
// packet! Therefore, do NOT touch any part of the
// IRP in the following code.
//
return;
}
}
else
{
if (Irp->PendingReturned && Irp->CurrentLocation <= Irp->StackCount)
{
IoMarkIrpPending( Irp );
}
ZeroIrpStackLocation( stackPointer );
}
}
3. 如果IRP有相应的Associated IRP,I/O管理器将递减AssociatedIrp.IrpCount字段的值,接着调用IopFreeIrpAndMdls()释放Associated IRP占用的内存和为它分配的MDL结构。如果是Master IRP的最后一个Associated IRP,I/O管理器直接对IRP本身调用IoCompleteRequest()函数。
//
// Check to see whether this is an associated IRP. If so, then decrement
// the count in the master IRP. If the count is decremented to zero,
// then complete the master packet as well.
//
if (Irp->Flags & IRP_ASSOCIATED_IRP)
{
ULONG count;
masterIrp = Irp->AssociatedIrp.MasterIrp;
count = ExInterlockedAddUlong( (PULONG) &masterIrp->AssociatedIrp.IrpCount,
0xffffffff,
&IopDatabaseLock );
//
// Deallocate this packet and any MDLs that are associated with it
// by either doing direct deallocations if they were allocated from
// a zone or by queueing the packet to a thread to perform the
// deallocation.
//
// Also, check the count of the master IRP to determine whether or not
// the count has gone to zero. If not, then simply get out of here.
// Otherwise, complete the master packet.
//
Irp->Tail.Overlay.Thread = masterIrp->Tail.Overlay.Thread;
IopFreeIrpAndMdls( Irp );
if (count == 1)
{
IoCompleteRequest( masterIrp, PriorityBoost );
}
return;
}
由Win2K源代码可以看出,还有一些中间的资源释放过程:
//
// Check to see if we have a name junction. If so set the stage to
// transmogrify the reparse point data in IopCompleteRequest.
//
if ((Irp->IoStatus.Status == STATUS_REPARSE ) &&
(Irp->IoStatus.Information > IO_REPARSE_TAG_RESERVED_RANGE))
{
if (Irp->IoStatus.Information == IO_REPARSE_TAG_MOUNT_POINT)
{
//
// For name junctions, we save the pointer to the auxiliary
// buffer and use it below.
//
ASSERT( Irp->Tail.Overlay.AuxiliaryBuffer != NULL );
saveAuxiliaryPointer = (PVOID) Irp->Tail.Overlay.AuxiliaryBuffer;
//
// We NULL the entry to avoid its de-allocation at this time.
// This buffer get deallocated in IopDoNameTransmogrify
//
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
}
else
{
//
// Fail the request. A driver needed to act on this IRP prior
// to getting to this point.
//
Irp->IoStatus.Status = STATUS_IO_REPARSE_TAG_NOT_HANDLED;
}
}
//
// Check the auxiliary buffer pointer in the packet and if a buffer was
// allocated, deallocate it now. Note that this buffer must be freed
// here since the pointer is overlayed with the APC that will be used
// to get to the requesting thread's context.
//
if (Irp->Tail.Overlay.AuxiliaryBuffer)
{
ExFreePool( Irp->Tail.Overlay.AuxiliaryBuffer );
Irp->Tail.Overlay.AuxiliaryBuffer = NULL;
}
4.很多I/O管理器执行的IRP事后处理都发生在请求I/O操作的线程上下文中。为了做到这一点,I/O管理器需要使用一个队列APC,该APC将在请求线程上下文中执行。然而这种方法并不能针对各种IRP请求结构,例如对一下类型的IRP请求。
Close操作:
当内核模式对象的引用变为0时, I/O管理器产生描述Close操作的IRP,并且将其发送到内核驱动。当某些内核模式APC执行时,由于这些APC可能使某些内核对象的引用变为0,因此可能触发Close操作。为了执行Close操作,I/O管理器调用一个内部函数IopCloseFile()。该函数采用同步模式执行,它首先分配和发布一个Close IRP请求到内核模式驱动,然后等待该事件的完成。因此当完成该Close IRP时,I/O管理器仅仅将返回状态拷贝出来,将IopCloseFie()等待的事件对象设置为0,接着程序返回。IopCloseFile()接着删除相应的IRP。
PagingI/O请求:
I/O管理器代表NT VMM(虚拟内存管理器)发布Paging I/O请求。在完成Paging I/O请求时,I/O管理器不能引入页错误,否则会导致系统崩溃,因此当I/O管理器完成Paging I/O请求时,将会做一下的事情:
-对于同步Paging I/O请求而言,I/O管理器将返回的I/O状态拷贝到用户提供的I/O状态块结构中,将调用者等待的内核事件对象置为信号态,释放IRP并且返回控制,没有其他的附加处理
-对于异步Paging I/O请求而言,I/O管理器将会排队一个特殊的APC,该APC将在请求Paging I/O的线程上下文中执行。该线程其实页就是Modified Page Writer(MPW)线程,它也是VMM子系统的组件之一。APC调度的程序将Paging 读操作的结构状态拷贝到MPW提供的I/O状态块中,然后使用另外一个内核APC调用一个MPW的Completion Routine。
//
// Check to see if this is paging I/O or a close operation. If either,
// then special processing must be performed. The reasons that special
// processing must be performed is different based on the type of
// operation being performed. The biggest reasons for special processing
// on paging operations are that using a special kernel APC for an in-
// page operation cannot work since the special kernel APC can incur
// another pagefault. Likewise, all paging I/O uses MDLs that belong
// to the memory manager, not the I/O system.
//
// Close operations are special because the close may have been invoked
// because of a special kernel APC (some IRP was completed which caused
// the reference count on the object to become zero while in the I/O
// system's special kernel APC routine). Therefore, a special kernel APC
// cannot be used since it cannot execute until the close APC finishes.
//
// The special steps are as follows for a synchronous paging operation
// and close are:
//
// 1. Copy the I/O status block (it is in SVAS, nonpaged).
// 2. Signal the event
// 3. If paging I/O, deallocate the IRP
//
// The special steps taken for asynchronous paging operations (out-pages)
// are as follows:
//
// 1. Initialize a special kernel APC just for page writes.
// 1. Queue the special kernel APC.
//
// It should also be noted that the logic for completing a Mount request
// operation is exactly the same as a Page Read. No assumptions should be
// made here about this being a Page Read operation w/o carefully checking
// to ensure that they are also true for a Mount. That is:
//
// IRP_PAGING_IO and IRP_MOUNT_COMPLETION
//
// are the same flag in the IRP.
//
// Also note that the last time the IRP is touched for a close operation
// must be just before the event is set to the signaled state. Once this
// occurs, the IRP can be deallocated by the thread waiting for the event.
//
if (Irp->Flags & (IRP_PAGING_IO | IRP_CLOSE_OPERATION))
{
if (Irp->Flags & (IRP_SYNCHRONOUS_PAGING_IO | IRP_CLOSE_OPERATION))
{
ULONG flags;
flags = Irp->Flags & IRP_SYNCHRONOUS_PAGING_IO;
*Irp->UserIosb = Irp->IoStatus;
(VOID) KeSetEvent( Irp->UserEvent, PriorityBoost, FALSE );
if (flags)
{
IoFreeIrp( Irp );
}
}
else
{
thread = Irp->Tail.Overlay.Thread;
KeInitializeApc( &Irp->Tail.Apc,
&thread->Tcb,
Irp->ApcEnvironment,
IopCompletePageWrite,
(PKRUNDOWN_ROUTINE) NULL,
(PKNORMAL_ROUTINE) NULL,
KernelMode,
(PVOID) NULL );
(VOID) KeInsertQueueApc( &Irp->Tail.Apc,
(PVOID) NULL,
(PVOID) NULL,
PriorityBoost );
}
return;
}
接着I/O管理器释放IRP关联的MDL,而对于Paging I/O操作,MDL结构属于VMM,因此它们由VMM在IRP完成的时候释放。
Mount请求:
查看NT DDK可以发现Paging I/O请求和Mount 请求(IRP_PAGING_IO,IRP_MOTJNT_COMPLETION)的值相等。这是因为I/O管理器把Mount当作同步Paging I/O读请求。因此对于Mount 请求,I/O管理器也进行和同步Paging I/O相同的事后处理。
5. 对于非Paging I/O、Close或者Mount请求而言,I/O管理器将解锁定由MDL描述的页。但是此时MDL并没有释放。它们将在请求线程的上下文中,做事后处理时释放。
//
// Check to see whether any pages need to be unlocked.
//
if (Irp->MdlAddress != NULL)
{
//
// Unlock any pages that may be described by MDLs.
//
mdl = Irp->MdlAddress;
while (mdl != NULL) {
MmUnlockPages( mdl );
mdl = mdl->Next;
}
}
6. 到目前为止I/O管理器已经处理了大部分的事后处理,这些事后处理都不是在请求线程中完成的。因此I/O管理器将在请求线程中排队一个特殊的内核APC。该APC将调用I/O管理器的一个内部函数IopCompleteRequest()。如果某个线程开始异步I/O操作之后退出了,而异步操作请求已经在底层驱动初始化完毕,系统可能就不知道将APC发往哪个线程上下文。这种情况下I/O管理器将放弃该请求,它将释放为IRP分配的内存,不再进行更进一步的处理。
对于同步Paging I/O而言,I/O管理器并不排队特殊的内核APC,而是简单的返回程序。这些IRP中的Flags字段被标记为IRP_DEFER_IO_COMPLETION。可以被延迟的IRP主要有:目录控制操作、读操作、写操作、创建/打开请求、查询文件信息、设置文件信息请求。I/O管理器直接返回控制可以避免内核APC排队带来的性能影响。相反,这些直接调用IoCallDriver()请求I/O操作的线程直接在程序返回时调用IopCompleteRequest()。这样可以带来性能优化。
I/O管理器根据一下条件判断释放排队APC:
-IRP_DEFER_IO_COMPLETION设置为TRUE
-Irp->PengdingReturned字段设定为FALSE
只有上面条件都满足时,I/O管理器才从IopCompleteRequest()中返回
//
// 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.
//
if (Irp->Flags & IRP_DEFER_IO_COMPLETION && !Irp->PendingReturned)
{
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;
}
//
// Finally, initialize the IRP as an APC structure and queue the special
// kernel APC to the target thread.
//
thread = Irp->Tail.Overlay.Thread;
fileObject = Irp->Tail.Overlay.OriginalFileObject;
if (!Irp->Cancel)
{
KeInitializeApc( &Irp->Tail.Apc,
&thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
IopAbortRequest,
(PKNORMAL_ROUTINE) NULL,
KernelMode,
(PVOID) NULL );
(VOID) KeInsertQueueApc( &Irp->Tail.Apc,
fileObject,
(PVOID) saveAuxiliaryPointer,
PriorityBoost );
} else {
//
// This request has been cancelled. Ensure that access to the thread
// is synchronized, otherwise it may go away while attempting to get
// through the remainder of completion for this request. This happens
// when the thread times out waiting for the request to be completed
// once it has been cancelled.
//
// Note that it is safe to capture the thread pointer above, w/o having
// the lock because the cancel flag was not set at that point, and
// the code that disassociates IRPs must set the flag before looking to
// see whether or not the packet has been completed, and this packet
// will appear to be completed because it no longer belongs to a driver.
//
ExAcquireSpinLock( &IopCompletionLock, &irql );
thread = Irp->Tail.Overlay.Thread;
if (thread)
{
KeInitializeApc( &Irp->Tail.Apc,
&thread->Tcb,
Irp->ApcEnvironment,
IopCompleteRequest,
IopAbortRequest,
(PKNORMAL_ROUTINE) NULL,
KernelMode,
(PVOID) NULL );
(VOID) KeInsertQueueApc( &Irp->Tail.Apc,
fileObject,
(PVOID) saveAuxiliaryPointer,
PriorityBoost );
ExReleaseSpinLock( &IopCompletionLock, irql );
}
else
{
//
// This request has been aborted from completing in the caller's
// thread. This can only occur if the packet was cancelled, and
// the driver did not complete the request, so it was timed out.
// Attempt to drop things on the floor, since the originating thread
// has probably exited at this point.
//
ExReleaseSpinLock( &IopCompletionLock, irql );
ASSERT( Irp->Cancel );
//
// Drop the IRP on the floor.
//
IopDropIrp( Irp, fileObject );
}
下面的情况可能导致驱动出现问题:
-驱动在将请求传递给下一层之前设定了Completion Routine
-在你的驱动上面还有其他的驱动
-你的驱动并没有执行if(Irp->PendingReturned==true) IoMarkIrpPending()代码
上面情况中,I/O管理器可能误以为不需要设置APC,这样会导致请求线程永远被阻塞。
另一种不需要排队APC的情况时文件对象关联了一个Completion Port。这种情况下I/O管理器会向该Completion Port发送一个消息。
下面的步骤发生在请求I/O操作的线程上下文中进行,对应的函数为IopCompleteRequest()(也在iostubs.c中)
VOID IopCompleteRequest(
IN PKAPC Apc,
IN PKNORMAL_ROUTINE *NormalRoutine,
IN PVOID *NormalContext,
IN PVOID *SystemArgument1,
IN PVOID *SystemArgument2
)
/*++
Routine Description:
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.
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.
Arguments:
Apc - Supplies a pointer to kernel APC structure.
NormalRoutine - Supplies a pointer to a pointer to the normal function
that was specified when the APC was initialied.
NormalContext - Supplies a pointer to a pointer to an arbitrary data
structure that was specified when the APC was initialized.
SystemArgument1 - Supplies a pointer to an argument that contains the
address of the original file object for this I/O operation.
SystemArgument2 - Supplies a pointer to an argument that contains an
argument that is used by this routine only in the case of STATUS_REPARSE.
Return Value:
None.
--*/
1.对于缓冲的I/O操作,I/O管理器将成功执行返回的数据复制到调用者提供的缓冲区。如果驱动返回错误或者在IRP的IoStatus中返回一个需要Verify操作的代码,将不进行拷贝操作
复制到调用者缓冲区的字节数等于IoStatus结构体中的Information。拷贝结束后,I/O管理器分配的内存将释放。
//
// Check to see whether there is any data in a system buffer which needs
// to be copied to the caller's buffer. If so, copy the data and then
// free the system buffer if necessary.
//
if (irp->Flags & IRP_BUFFERED_IO)
{
//
// Copy the data if this was an input operation. Note that no copy
// is performed if the status indicates that a verify operation is
// required, or if the final status was an error-level severity.
//
if (irp->Flags & IRP_INPUT_OPERATION &&
irp->IoStatus.Status != STATUS_VERIFY_REQUIRED &&
!NT_ERROR( irp->IoStatus.Status ))
{
//
// Copy the information from the system buffer to the caller's
// buffer. This is done with an exception handler in case
// the operation fails because the caller's address space
// has gone away, or it's protection has been changed while
// the service was executing.
//
try
{
RtlCopyMemory( irp->UserBuffer,
irp->AssociatedIrp.SystemBuffer,
irp->IoStatus.Information );
}
except(IopExceptionFilter(GetExceptionInformation(), &status))
{
//
// An exception occurred while attempting to copy the
// system buffer contents to the caller's buffer. Set
// a new I/O completion status.
// If the status is a special one set by Mm then we need to
// return here and the operation will be retried in
// IoRetryIrpCompletions.
//
if (status == STATUS_MULTIPLE_FAULT_VIOLATION)
{
/* Wiped out by APC overlay */
irp->Tail.Overlay.OriginalFileObject = fileObject;
irp->Flags |= IRP_RETRY_IO_COMPLETION;
return;
}
irp->IoStatus.Status = GetExceptionCode();
}
}
//
// Free the buffer if needed.
//
if (irp->Flags & IRP_DEALLOCATE_BUFFER) {
ExFreePool( irp->AssociatedIrp.SystemBuffer );
}
}
irp->Flags &= ~(IRP_DEALLOCATE_BUFFER|IRP_BUFFERED_IO);
2.IRP关联的所有的MDL都被释放
//
// If there is an MDL (or MDLs) associated with this I/O request,
// Free it (them) here. This is accomplished by walking the MDL list
// hanging off of the IRP and deallocating each MDL encountered.
//
if (irp->MdlAddress)
{
for (mdl = irp->MdlAddress; mdl != NULL; mdl = nextMdl)
{
nextMdl = mdl->Next;
IoFreeMdl( mdl );
}
}
irp->MdlAddress = NULL;
3.I/O管理器将Status和Information字段复制到调用者提供的I/O状态块结构体。
try {
//
// Since HasOverlappedIoCompleted and GetOverlappedResult only
// look at the Status field of the UserIosb to determine if the
// IRP has completed, the Information field must be written
// before the Status field.
//
#if defined(_M_ALPHA) && !defined(NT_UP)
#define MEMORY_BARRIER() __MB()
#else
#define MEMORY_BARRIER()
#endif
#if defined(_WIN64)
PIO_STATUS_BLOCK32 UserIosb32;
//
// If the caller passes a 32 bit IOSB the ApcRoutine has the LSB set to 1
//
if ((ULONG_PTR)(irp->Overlay.AsynchronousParameters.UserApcRoutine) & 1)
{
UserIosb32 = (PIO_STATUS_BLOCK32)irp->UserIosb;
UserIosb32->Information = (ULONG)irp->IoStatus.Information;
MEMORY_BARRIER();
UserIosb32->Status = (NTSTATUS)irp->IoStatus.Status;
}
else
{
irp->UserIosb->Information = irp->IoStatus.Information;
MEMORY_BARRIER();
irp->UserIosb->Status = irp->IoStatus.Status;
}
#else
irp->UserIosb->Information = irp->IoStatus.Information;
MEMORY_BARRIER();
irp->UserIosb->Status = irp->IoStatus.Status;
#endif /*_WIN64 */
}
4.如果调用者提供了一个事件对象,I/O管理器将该事件设置为信号态。如果I/O操作为同步操作或者用户没有提供事件对象,I/O管理器将把IRP关联的文件对象的Event字段的事件设置为信号态。
if (irp->UserEvent)
{
(VOID) KeSetEvent( irp->UserEvent, 0, FALSE );
if (fileObject)
{
if (!(irp->Flags & IRP_SYNCHRONOUS_API))
{
ObDereferenceObject( irp->UserEvent );
}
if (fileObject->Flags & FO_SYNCHRONOUS_IO && !(irp->Flags & IRP_OB_QUERY_NAME))
{
(VOID) KeSetEvent( &fileObject->Event, 0, FALSE );
fileObject->FinalStatus = irp->IoStatus.Status;
}
if (irp->Flags & IRP_CREATE_OPERATION)
{
createOperation = TRUE;
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
}
}
} else if (fileObject)
{
(VOID) KeSetEvent( &fileObject->Event, 0, FALSE );
fileObject->FinalStatus = irp->IoStatus.Status;
if (irp->Flags & IRP_CREATE_OPERATION)
{
createOperation = TRUE;
irp->Overlay.AsynchronousParameters.UserApcRoutine = (PIO_APC_ROUTINE) NULL;
}
}
5.I/O管理器将它们使用过的对象引用计数减1
6.I/O管理器从当前线程的Pending IRP队列中删除该IRP
//
// Dequeue the packet from the thread's pending I/O request list.
//
IopDequeueThreadIrp( irp );
7.最终释放该IRP对应的内存
//
// If the caller requested an APC, queue it to the thread. If not, then
// simply free the packet now.
//
#ifdef _WIN64
//
// For 64 bit systems clear the LSB field of the ApcRoutine that indicates whether
// the IOSB is a 32 bit IOSB or a 64 bit IOSB.
//
irp->Overlay.AsynchronousParameters.UserApcRoutine =
(PIO_APC_ROUTINE)((LONG_PTR)(irp->Overlay.AsynchronousParameters.UserApcRoutine) & ~1);
#endif
if (irp->Overlay.AsynchronousParameters.UserApcRoutine)
{
KeInitializeApc( &irp->Tail.Apc,
&thread->Tcb,
CurrentApcEnvironment,
IopUserCompletion,
(PKRUNDOWN_ROUTINE) IopUserRundown,
(PKNORMAL_ROUTINE) irp->Overlay.AsynchronousParameters.UserApcRoutine,
irp->RequestorMode,
irp->Overlay.AsynchronousParameters.UserApcContext );
KeInsertQueueApc( &irp->Tail.Apc,
irp->UserIosb,
NULL,
2 );
}
else if (port && irp->Overlay.AsynchronousParameters.UserApcContext)
{
//
// If there is a completion context associated w/this I/O operation,
// send the message to the port. Tag completion packet as an Irp.
//
irp->Tail.CompletionKey = key;
irp->Tail.Overlay.PacketType = IopCompletionPacketIrp;
KeInsertQueue( (PKQUEUE) port, &irp->Tail.Overlay.ListEntry );
}
else
{
//
// Free the IRP now since it is no longer needed.
//
IoFreeIrp( irp );
}
if (fileObject && !createOperation)
{
//
// Dereference the file object now.
//
ObDereferenceObject( fileObject );
}
这篇关于《Windows NT FileSystem Internals》学习笔记之Complete IRP的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!