《Windows NT FileSystem Internals》学习笔记之Complete IRP

2024-02-07 03:58

本文主要是介绍《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_REQUESTbugcheck。如果安装的是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_REQUIREDI/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 IRPI/O管理器将递减AssociatedIrp.IrpCount字段的值,接着调用IopFreeIrpAndMdls()释放Associated IRP占用的内存和为它分配的MDL结构。如果是Master IRP的最后一个Associated IRPI/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调用一个MPWCompletion 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,因此它们由VMMIRP完成的时候释放。

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/OClose或者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管理器将成功执行返回的数据复制到调用者提供的缓冲区。如果驱动返回错误或者在IRPIoStatus中返回一个需要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管理器将StatusInformation字段复制到调用者提供的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的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/686452

相关文章

HarmonyOS学习(七)——UI(五)常用布局总结

自适应布局 1.1、线性布局(LinearLayout) 通过线性容器Row和Column实现线性布局。Column容器内的子组件按照垂直方向排列,Row组件中的子组件按照水平方向排列。 属性说明space通过space参数设置主轴上子组件的间距,达到各子组件在排列上的等间距效果alignItems设置子组件在交叉轴上的对齐方式,且在各类尺寸屏幕上表现一致,其中交叉轴为垂直时,取值为Vert

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

学习hash总结

2014/1/29/   最近刚开始学hash,名字很陌生,但是hash的思想却很熟悉,以前早就做过此类的题,但是不知道这就是hash思想而已,说白了hash就是一个映射,往往灵活利用数组的下标来实现算法,hash的作用:1、判重;2、统计次数;

零基础学习Redis(10) -- zset类型命令使用

zset是有序集合,内部除了存储元素外,还会存储一个score,存储在zset中的元素会按照score的大小升序排列,不同元素的score可以重复,score相同的元素会按照元素的字典序排列。 1. zset常用命令 1.1 zadd  zadd key [NX | XX] [GT | LT]   [CH] [INCR] score member [score member ...]

【机器学习】高斯过程的基本概念和应用领域以及在python中的实例

引言 高斯过程(Gaussian Process,简称GP)是一种概率模型,用于描述一组随机变量的联合概率分布,其中任何一个有限维度的子集都具有高斯分布 文章目录 引言一、高斯过程1.1 基本定义1.1.1 随机过程1.1.2 高斯分布 1.2 高斯过程的特性1.2.1 联合高斯性1.2.2 均值函数1.2.3 协方差函数(或核函数) 1.3 核函数1.4 高斯过程回归(Gauss

【学习笔记】 陈强-机器学习-Python-Ch15 人工神经网络(1)sklearn

系列文章目录 监督学习:参数方法 【学习笔记】 陈强-机器学习-Python-Ch4 线性回归 【学习笔记】 陈强-机器学习-Python-Ch5 逻辑回归 【课后题练习】 陈强-机器学习-Python-Ch5 逻辑回归(SAheart.csv) 【学习笔记】 陈强-机器学习-Python-Ch6 多项逻辑回归 【学习笔记 及 课后题练习】 陈强-机器学习-Python-Ch7 判别分析 【学

系统架构师考试学习笔记第三篇——架构设计高级知识(20)通信系统架构设计理论与实践

本章知识考点:         第20课时主要学习通信系统架构设计的理论和工作中的实践。根据新版考试大纲,本课时知识点会涉及案例分析题(25分),而在历年考试中,案例题对该部分内容的考查并不多,虽在综合知识选择题目中经常考查,但分值也不高。本课时内容侧重于对知识点的记忆和理解,按照以往的出题规律,通信系统架构设计基础知识点多来源于教材内的基础网络设备、网络架构和教材外最新时事热点技术。本课时知识

线性代数|机器学习-P36在图中找聚类

文章目录 1. 常见图结构2. 谱聚类 感觉后面几节课的内容跨越太大,需要补充太多的知识点,教授讲得内容跨越较大,一般一节课的内容是书本上的一章节内容,所以看视频比较吃力,需要先预习课本内容后才能够很好的理解教授讲解的知识点。 1. 常见图结构 假设我们有如下图结构: Adjacency Matrix:行和列表示的是节点的位置,A[i,j]表示的第 i 个节点和第 j 个

Node.js学习记录(二)

目录 一、express 1、初识express 2、安装express 3、创建并启动web服务器 4、监听 GET&POST 请求、响应内容给客户端 5、获取URL中携带的查询参数 6、获取URL中动态参数 7、静态资源托管 二、工具nodemon 三、express路由 1、express中路由 2、路由的匹配 3、路由模块化 4、路由模块添加前缀 四、中间件