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

Python爬虫:招聘网站岗位要求、薪资与求职者数据爬取与分析 通过本文的教程,我们学习了如何通过 Python 编写招聘数据爬虫,爬取招聘网站上的岗位要求、薪资和求职者数据,并通过数据存储、清洗、分析和可视化,获取有价值的信息。网站如智联招聘、猎云网、前程无忧等,都提供了大量的招聘信息,包括岗位要求、薪资范围、工作经验、学位要求、招聘公司等。本文将介绍如何通过 Python 编写爬虫,爬取招聘网站上的岗位要求、薪资、求职者数据,并通过数据存储、分析和可视化等手段,为求职者或招聘者提供实用的信息和决策支持。通过循环抓取多个页面的数据,可以获取不同岗位的信息。 阅读详情

by fengcaho

以为完成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 );

}

 
ROS2入门到精通—— 1-1 Linux和ROS2基础 本文介绍了Linux的一般指令以及ROS2的常见理论和操作 阅读详情

相关推荐

ROS中tf2_ros::Buffer.lookupTransform的正确使用姿势:从踩坑到填坑

本文深入解析了ROS中tf2_ros::Buffer.lookupTransform接口的正确使用方法,重点剖析了因tf2_ros::TransformListener生命周期管理不当导致的经典问题。文章提供了从理解Buffer与Listener共生关系、类成员设计黄金法则到参数详解与异常处理的完整避坑指南,并给出了构建健壮坐标变换工具类的实战方案,帮助开发者稳定实现机器人坐标系变换。

weixin_29253711的博客 137

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

原来以为完成IRP的处理仅仅是简单的调用一下IoCompleteRequest()函数而已,内部的代码应该很简单。阅读了Win2K源代码才发现,事情并不简单啊,呵呵,再一次发现Win2K源代码的价值了。驱动必须调用IoCompleteRequest()通知I/O管理器相应的IRP已经处理完毕。IoCompleteRequest函数的处理过程如下(对应着Win2K源代码阅读,在ioS

不论你在什么时候开始,重要的是开始之后就不要停止。 2647

CVE-2017-7921漏洞深度解析:海康设备明文密码泄露原理与七层防御

网络摄像头安全本质上是嵌入式Web服务安全,其核心在于身份认证机制是否健全。CVE-2017-7921并非传统远程执行漏洞,而是因信任错位导致的未授权API暴露——`/Security/users/1`接口绕过Session校验,直接返回admin明文密码。该问题根植于安防设备早期‘物理隔离+前端可控’的设计假设,在P2P、云平台等新场景下全面失效。技术价值在于揭示了轻量级HTTP服务中路径级访问控制缺失的普遍风险;典型应用场景包括ONVIF协议接管、Telnet后门登录及NVR侧横向移动。本文基于DS-2

weixin_33324197的博客 403

IoCompleteRequest的实现逻辑

#define IoCompleteRequest(a,b)  \         IofCompleteRequest(a,b) VOID FASTCALL IopfCompleteRequest(     IN PIRP Irp,     IN CCHAR PriorityBoost     ) /*++ Routine Description:

Rodney443220的专栏 1583

基于555定时器的D类功放设计:从PWM原理到无反馈电路实践

D类功放(数字功放)是一种高效率的音频放大技术,其核心原理是脉冲宽度调制(PWM)。它将输入的模拟音频信号转换为一系列高速开关的脉冲,通过控制功率开关管(如MOSFET)的通断,再经低通滤波器还原放大后的音频。相比传统AB类功放,其转换效率通常超过90%,显著降低了热能损耗。这项技术在追求高能效和小型化的现代音频设备中价值凸显,广泛应用于有源音箱、汽车音响和便携式设备。本文聚焦于一种独特的实现方案:以经典的555定时器芯片为核心,构建一个无整体负反馈的D类功放。通过剖析其PWM调制器、FET电压-电流转换级

weixin_30162121的博客 226

使用TensorFlow搭建FNN(全连接神经网络)的基本步骤

提示:转载请注明出处,若本文无意侵犯到您的合法权益,请及时与作者联系。 目录 一、定义神经网络的输入输出 二、搭建神经网络 1、定义一个神经层中的权重 2、定义一个神经层中的阈值 3、定义一个神经层中的激活函数 4、组合神经层为神经网络 三、搭建损失函数和优化器 1、搭建损失函数 首先介绍第一个函数这其中用到的共同的函数为 2、定义优化器 四、训练神经网络 全连接神经网络的网络结构如下: 一、定义神经网络的输入输出 神经网络最擅长的工作就是模拟非线性函数,即一个复杂的高..

qq_41959920的博客 4072

解决关机蓝屏 | MULTIPLE_IRP_COMPLETE_REQUESTS | klflt.sys

已经解决蓝屏问题,原因是卡巴斯基安全软件驱动导致,需要卸载卡巴斯基安全软件,详细过程如下。 一、关机时蓝屏 Win10系统,在关机动画快结束时突然蓝屏,提示:你的设备遇到问题,需要重启,终止代码:MULTIPLE_IRP_COMPLETE_REQUESTS。然后黑屏关机,无论是直接关机还是重启时都会蓝屏。 二、一些尝试 下面方法没有解决蓝屏,但可能对其他问题有帮助 尝试更新系统,卸载和安装驱动都没有解决。 使用了Dism命令修复,没有解决。鼠标右击左下角开始按钮-------"Windows Po.

hey_wei_ran的博客 4万+

05-SA8155 QNX Display框架及代码分析(1)

显示是非常重要的一个模块,人机交互的桥梁。那么SA8155 Hypervisor显示是如何处理的呢。对处理流程非常的好奇O(∩_∩)O哈哈~。TODO。

静虚待令,有物混成 1万+

IO完成端口

下面摘抄于MSDN《I/O Completion Ports》,smallfool翻译,原文请参考优快云文档中心文章《I/O Completion Ports》, http://dev.youkuaiyun.com/Develop/article/29%5C29240.shtm 。

544

Verilog的综合指令

Synopsys综合指令

烟花一时的博客 5447

三相五柱式消弧线圈自动测控装置设计与实现

本文设计了一种基于STM32的三相五柱式消弧线圈自动测控装置。该装置采用信号注入法检测系统对地电容电流,通过PID自适应算法调节消弧线圈电感值,实现单相接地故障时的快速补偿。硬件系统包含信号采集、注入、控制执行等模块,软件采用Goertzel算法提取特征频率信号,结合增量式PID实现精确控制。实验结果表明,装置电容检测误差小于2%,调谐响应时间小于50ms,满足配电网对消弧线圈自动调谐的技术要求。

AI的内部世界的博客 239

MULTIPLE_IRP_COMPLETE_REQUESTS 蓝屏Bug解决

调试驱动时遇到如上蓝屏问题。一番抓虫工作之后,终于解决,原来是代码中有个地方考虑得不够周全,某些情况下会导致IRP的重复完成从而引起系统蓝屏。 仔细看了下IRP的生存周期和处理流程,当驱动中调用IoCompleteRequest来完成某个IRP之后,I/O管理器将会释放掉这个IRP,这样的一来,后续再对该IRP执行IoCompleteRequest时,必然导致如上错误。

门没锁的专栏 3万+

【信息系统项目管理师】高分论文:论信息系统项目的整合管理(绩效考核系统)

我们将项目管理计划中的子计划包括项目范围、进度、成本、质量管理计划,过程改进计划,人力资源管理计划,风险、采购管理计划,干系人管理计划,项目范围、成本、进度基准作为依据,查看项目的各个方面,通过组织全体团体成员会议来解读由各监控过程提供的信息,之后我与项目管理团队一起制订所需措施,确保项目绩效达到预期要求。在一个项目中,变更是必然的,也是不可避免的,进度拖延,成本失控,客户有新的需求等都会引起项目的变更。在本项目中,我要求有处于工作状态的产品,开发人员才可以对其任意修改,修改完成后,重新入库即可。

数据知道的博客 6888

完成端口(I/O completion)

 完成端口(I/O completion)异步过程调用(apcs)问题:只有发overlapped请求的线程才可以提供callback函数(需要一个特定的线程为一个特定的I/O请求服务)。完成端口(I/O completion)的优点:不会限制handle个数,可处理成千上万个连接。I/O completion port允许一个线程将一个请求暂时保存下来,由另一个线程为它做实际服务。并发模型与

905

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

IOCP原理

简介I/O完成端口是为多处理器系统处理多重异步I/O请求线程模型。 I/O完成端口就是与线程池配合使用的。APICreateIoCompletetionPort 创建IOCP和关联一个或多个句柄到端口上。 GetQueuedCompletionStatus 等待或从IOCP队列中取出完成包(completion packet)。 PostQueuedCompletionStatus 把一个完成

694

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

Windows蓝屏代码

      代码 含意  0 0x0000 作业完成。  1 0x0001 不正确的函数。  2 0x0002 系统找不到指定的档案。  3 0x0003 系统找不到指定的路径。  4 0x0004 系统无法开启档案。  5 0x0005 拒绝存取。  6 0x0006 无效的代码。  7 0x0007 储存体控制区块已毁。  8 0x0008 储存体空间不足,

regina的专栏 1678

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

Chrome 引起的蓝屏 MULTIPLE_IRP_COMPLETE_REQUESTS (44)

如果你使用Chrome的时候出现经常性蓝屏, 可以试试这么做, 或许问题就解决了. 转载于:https://www.cnblogs.com/nikosoft/p/5106710.html

weixin_30379911的博客 5082

IRP

IRP IRP 结构体部分不透明,它代表了一个IO请求包。驱动程序可以使用结构的以下成员: typedef struct _IRP {   .   .   PMDL  MdlAddress;   ULONG  Flags;   union {     struct _IRP  *MasterIrp;     .     .     PVOID  SystemBuffer;

死胖子的博客 3942

Windows 11蓝屏0x44:关机重启排查与修复

这次蓝屏案例,最终处理路径可以总结为:fill:#333;important;important;fill:none;color:#333;color:#333;important;fill:none;fill:#333;height:1em;未恢复用户反馈蓝屏可靠性监视器确认 BlueScreen收集 C:\Windows\MinidumpWhoCrashed 分析 BugCheck 0x44判断偏驱动层/系统底层异常PowerShell 收集事件和驱动信息尝试驱动更新和系统内修复。

分享 Windows 系统部署、镜像封装、终端管理、故障排查与运维自动化实践,涵盖 Sysinternals、PowerShell 和 AI 工具应用。记录真实问题、探索过程与解决方法,也分享 AI 时代的学习心得和实践经验,让技术更易理解 11万+

文件重定向(采用文件过滤驱动实现)

原文:http://hi.baidu.com/tzpwater/blog/item/9bf66eb5196b1fe130add132.html/cmtid/65f21515aea03006c83d6d41Windows的I/O管理器提供了一个方便的方法来重定向一个文件对象。通常使用文件过滤驱动(在文件打开和文件创建的操作中)实现该方法。操作方法如下:1、在IRP_MJ_CREATE的分发函

myworldbig的专栏 5131

IopfCompleteRequest源码

不知道哪位大神的 正需要 转载在这里 VOID FASTCALL IopfCompleteRequest( IN PIRP Irp, IN CCHAR PriorityBoost )

sqqsongqiqi的专栏 1514

对内核函数IoCompleteRequest的分析

该文来自看雪   对内核函数IoCompleteRequest的分析 作 者: RYYMike 原文地址 http://bbs.pediy.com/showthread.php?t=109018 今天是我的18周岁生日,突然就想发表篇文章,而前几天刚好在做IopfCompleteRequest的分析,所以今天就赶赶急,把分析做做完,发表出来。       我在网上认识的一个前辈说

sqqsongqiqi的专栏 1481

IRP结构

IRP结构 图5-1显示了IRP的数据结构,阴影部分代表不透明域。下面是该结构中重要域的简要描述。 MdlAddress(PMDL)域指向一个内存描述符表(MDL),该表描述了一个与该请求关联的用户模式缓冲区。如果顶级设备对象的Flags域为DO_DIRECT_IO,则I/O管理器为IRP_MJ_READ或 IRP_MJ_WRITE请求创建这个MDL。如果一个IRP_MJ_DEVICE

木易云清 810

IRP IO_STACK_LOCATION 《寒江独钓》内核学习笔记(1)

在学习内核过滤驱动的过程中,遇到了大量的涉及IRP操作的代码,这里有必要对IRP的数据结构和与之相关的API函数做一下笔记。   1. 相关阅读资料 《深入解析 windows 操作系统(第4版,中文版)》 --- 9章 《windows driver kit 帮助文档》 http://support.microsoft.com/kb/115758/zh-cn  IRP 结构中各地址字段...

weixin_33739523的博客 2504

IoCompleteRequest深入学习之二

 接上篇还原为c的代码如下:VOID FASTCALL IopfCompleteRequest(                                    IN PIRP Irp,                                    IN CCHAR PriorityBoost                                ){    UCHAR uC

windowzhong的专栏 1794

用C++进行驱动开发

驱动开发主要用C, 也可以用C++,还是用C好,因为好多工具只支持C, 如Static Driver Verifier. 如何用C++来开发呢, 以下引用一段代码. #ifndef DRIVER_H#define DRIVER_H#pragma warning (disable:4200) extern "C" {#include <ntddk....

weixin_34248023的博客 1796
上一篇: 驱动开发之二:尝试挂接file system
下一篇: Windows NT FileSystem Internals》学习笔记之IO_STACK_LOCATION的结构
iiprogram
博客等级 码龄25年 1068粉丝 1515原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值