Winoows内核设计思想之IRP
+ -

驱动的卸载流程IRP_MN_REMOVE_DEVICE与其余IRP竞争

2023-07-28 12 0

USB通用总线驱动程序源代码USBC_Dispatch函数中有这一段代码:

    if (!((majorFunction == IRP_MJ_PNP) && (minorFunction == IRP_MN_REMOVE_DEVICE)))
    {
        IncrementPendingActionCount(parentFdoExt);
    }

这段代码应该完全可以写成:

    if (!(minorFunction == IRP_MN_REMOVE_DEVICE))
    {
        IncrementPendingActionCount(parentFdoExt);
    }

    if (minorFunction != IRP_MN_REMOVE_DEVICE)
    {
        IncrementPendingActionCount(parentFdoExt);
    }

最终简化为:当该IRP不是主功能号为:IRP_MJ_PNP,次功能号为IRP_MN_REMOVE_DEVICE,计数加1。

当然,在IRP完成后,应:

    /*
     *  Balance the increment above
     */
    if (!((majorFunction == IRP_MJ_PNP) && (minorFunction == IRP_MN_REMOVE_DEVICE)))
    {
        DecrementPendingActionCount(parentFdoExt);
    }

所以,对于该驱动中的所有IRP,除过IRP_MN_REMOVE_DEVICE,都进行了计数平衡。

   driverObj->MajorFunction[IRP_MJ_CREATE] =
        driverObj->MajorFunction[IRP_MJ_CLOSE] =
        driverObj->MajorFunction[IRP_MJ_DEVICE_CONTROL] =
        driverObj->MajorFunction[IRP_MJ_SYSTEM_CONTROL] =
        driverObj->MajorFunction[IRP_MJ_INTERNAL_DEVICE_CONTROL] =
        driverObj->MajorFunction[IRP_MJ_PNP] =
        driverObj->MajorFunction[IRP_MJ_POWER] = USBC_Dispatch;

那么,为什么要讲行计数平衡了,它的意义是什么。微软给出的解释为:

    /*
     *  For all IRPs except REMOVE, we increment the PendingActionCount
     *  across the dispatch routine in order to prevent a race condition with
     *  the REMOVE_DEVICE IRP (without this increment, if REMOVE_DEVICE
     *  preempted another IRP, device object and extension might get
     *  freed while the second thread was still using it).
     */

它的大概含义是由于IPR竞争。这主要是因为内核是多线程的。个人感觉是应用层的线程可能还有携带IPR,但系统卸载线程的卸载已经下发。这时由于多线程或时间片关系,可能会产生竞争(2个IRP时间片交叉)。即可能IRP_MN_REMOVE_DEVICE已经执行,但其它的线程还占用设备驱动。故在卸载时同步一下。

对于该计数pendingActionCount,我们可以搜索驱动整个代码:
发现计数减1时的代码如下:

    ASSERT(parentFdoExt->pendingActionCount >= 0);
    InterlockedDecrement(&parentFdoExt->pendingActionCount);    

    if (parentFdoExt->pendingActionCount < 0)
    {
        /*
         *  All pending actions have completed and we've gotten
         *  the REMOVE_DEVICE IRP.
         *  Set the removeEvent so we'll stop waiting on REMOVE_DEVICE.
         */
        ASSERT((parentFdoExt->state == STATE_REMOVING) || 
               (parentFdoExt->state == STATE_REMOVED));
        KeSetEvent(&parentFdoExt->removeEvent, 0, FALSE);
    }

所以关联的是removeEvent事件。而该事件关联于函数IRP_MN_REMOVE_DEVICE的PrepareParentFDOForRemove。

           case IRP_MN_REMOVE_DEVICE:

                /*
                 *  Cancel downward IO
                 *  Set default status to SUCCESS
                 *  Send the IRP down
                 *  Detach
                 *  Cleanup
                 */
                PrepareParentFDOForRemove(parentFdoExt);

                irp->IoStatus.Status = STATUS_SUCCESS;
                IoSkipCurrentIrpStackLocation(irp);
                status = IoCallDriver(parentFdoExt->topDevObj, irp);
                irpAlreadyCompleted = TRUE;

                IoDetachDevice(parentFdoExt->topDevObj);

                FreeParentFDOResources(parentFdoExt);

                break;

而PrepareParentFDOForRemove中:

  • 卸载子设备FreeFunctionPDOResources
  • DecrementPendingActionCount 后,再事件(必达)

最后总结一下:

  • 在该驱动中,必要的所有IPR请求计数,以标识驱动是否为空闲态。
  • 卸载例程中,首先释放的是子设备的资源,其次计数减1,等待事件(一般必达),但也有可能减1后的线程切换,导致别的IPR还在工作,故待一个事件,这个事件在别的IPR完成后会减计数。当收到事件后,再发下给下层设备等其完成后再释放FDO总线资源。

驱动的卸载流程IRP_MN_REMOVE_DEVICE与其余IRP竞争

0 篇笔记 写笔记

自定义IRP的回收利用
前面说到,我们有时需要自已生成新的IPR并派发到下层驱动,所以这里的IRP派发一般的情况下是频繁的,所以上节中会出现频敏的IoAllocateIrp和IoFreeIrp。这样会造成性能上的损失,那有没有更好的处理方式了。答案是肯定的:在说答案之前,我们先介绍一个函数:IoReuseIrp,其REAC......
IRP的完成IoCompleteRequest
每当一个IRP在下层设备层完成时,是需要调用IoCompleteRequest来实现IRP的完成,这个完成其实是实现对执行的IRP的善后操作,这个操作其实是一个宏,真实函数数是IofCompleteRequest。#define IoCompleteRequest IofCompleteReque......
Windows IRP结构字段解释
 比如说你开发了一个USB接口的设备, 那么为了在windows上使用它, 你需要开发一个驱动程序,也许还需要一个应用程序.有些硬件是"免驱"的.但事实上不是不需要驱动,而是他所需要的驱动已经存在在windows中了,不需要你另外安装而已.  你使用应用程序可以控制该硬件的行为,过程是应用程序发出......
自定义IRP的完成与处理
一般情况下,当我们在windows驱动中收到IRP时,驱动根据实际情况分为几种情况:第一种是直接完成填充相关的参数后,直接调用IoCompleteRequest完成。第二种情况是该层做相关的设置,如设置完成例程,然后调用IoCallDriver进行下发。在第二种情况下,一般是当在IRP_MN_STA......
IRP与LIST_ENTRY的关联使用
IRP是Windows内核中的一个很重要的概念,代表着应用或内核对设备的请求。LIST_ENTRY又是Windows内核提供的一个链表。IRP挂入LIST_ENTRYVOID RecyleFrame(PFDO_DEVICE_EXTENSION deviceExtension, FrameHead......
IoCopyCurrentIrpStackLocationToNext和IoSkipCurrentIrpStackLocation操作的IO_STACK_LOCATION有什么区别
在Windows驱动中,传递IPR一般有两种操作:一种是调用IoSkipCurrentIrpStackLocation,表示跳过本层驱动的操作,直接转发至下层: IoSkipCurrentIrpStackLocation(Irp); return IoCallDriver(FDODeviceEx......
IRP完成APC执行函数IopCompleteRequest
IRP在完成时调用IoCompleteRequest,其最终会执行一个APC调用,该调用的函数名为IopCompleteRequest。其调用APC调用时的代码如下:KeInitializeApc(&Irp->Tail.Apc, &......
WDDM 停止、复位、移除设备及驱动卸载
说完了驱动的创建与启动,这里顺使说一下设备的停止、复位、卸载设备设备的停止/BddDdiStopDevice设备停止的回调函数为BddDdiStopDevice InitialData.DxgkDdiStopDevice = BddDdiStopDevi......
IRP完成与IO_STACK_LOCATION
IRP与IO_STACK_LOCATION的关系IPR完成时,有时为了获取数据,我们经常要这样干 IoCopyCurrentIrpStackLocationToNext(Irp); IoSetCompletionRoutine( Irp, Comple......
IRP无法取消的无限等待问题
有时有这样一种功能,就是当上层设备或应用和我们开发的驱动的时候,而我们这时根本无法完成本次的IRP,所以根据WINDOWS 驱动开发规范,我们这时需要对该IRP进行MarkPending,然后返回Pending。NTSTATUSMyDispatch( PDEVICE_OBJECT Dev......
IRP完成例程IoSetCompletionRoutine的设计和实现原理
在进行IRP下层传递时,通过上一节可知道,一种中使用IoCopyCurrentIrpStackLocationToNext,另一种是IoSkipCurrentIrpStackLocation。其中在使用IoCopyCurrentIrpStackLocationToNext表示的是对当前的IRP当留当......
IRP的取消机制IoCancelIrp
在驱动层,可以使用取消例程实现对IRP的取消。IRP的取消主要涉及几个函数,分别是:IoSetCancelRoutine 设置取消IRP例程IoCancelIrp函数看起来比较简单,但过程却是并不简单。REACTOS提供的IoCancelIrp的源代码如下:BOOLEAN NTAPI IoC......
Windows x64 IRP结构体成员偏移地址
4: kd> dt _IRPntdll!_IRP +0x000 Type : Int2B +0x002 Size : Uint2B +0x004 AllocationProcessorNumber : Uint2B +0......
PDO设备的动态创建与卸载IRP_MN_SURPRISE_REMOVAL和IRP_MN_REMOVE_DEVICE
总线驱动创建的PDO在卸载时,因卸载方式不同而不同如果是直接卸载总线,会只调用IRP_MN_REMOVE_DEVICE而如果是动态创建与卸载,在卸载的时候调用IoInvalidateDeviceRelations会导致IRP_MN_SURPRISE_REMOVAL的调用,然后调用IRP_MN_R......
挂起的IPR在卸载驱动中的处理
有时有一种情况,说是会将上层如CAMERA,HID等USB设备下发的IPR进行入链表LIST_ENTRY,当然在入之前,会将此IRP 挂起pending,并设置取消例程。不过在驱动卸载的时候,或者某种情况下需要手动清除这些IRP时,会出现和CANCEL例程竞争的情况。故这些需要进行一些判断再处理:......
作者信息
我爱内核
Windows驱动开发,网站开发
好好学习,天天向上。
取消
感谢您的支持,我会继续努力的!
扫码支持
扫码打赏,你说多少就多少

打开支付宝扫一扫,即可进行扫码打赏哦

您的支持,是我们前进的动力!