【问题标题】:WDM device removal in PnP driver在 PnP 驱动程序中删除 WDM 设备
【发布时间】:2017-08-01 15:22:24
【问题描述】:

我对移除设备有疑问。

当我们想通知 PnP 管理器设备已消失时,我们会调用 IoInvalidateDeviceRelationsBusRelations。之后操作系统将使用BusRelations 发送IRP_MN_QUERY_DEVICE_RELATIONS 请求。在这个请求处理程序中,我们将从数组中排除设备,并将做另一项必要的工作以“断开”它与总线的连接,我们还将在其设备扩展中设置 RemovePending 标志。

我不明白在设备变为“删除待处理”之后和操作系统发送IRP_MN_REMOVE_DEVICE 请求之前如何处理传入设备的 IO 请求。我们应该检查RemovePending 标志并返回STATUS_DEVICE_DOES_NOT_EXIST 还是应该照常进行?

现在想象IRP_MN_REMOVE_DEVICE 请求终于到达了。 MSDN说我们必须调用IoReleaseRemoveLockAndWait来释放当前获取的remove lock,防止后续获取并等待现有获取被释放。所以它迫使我们总是在 PnP 请求处理程序中使用IoAcquireRemoveLock 获取删除锁;并使用IoReleaseRemoveLockAndWait 释放它以获取IRP_MN_REMOVE_DEVICE 或使用IoReleaseRemoveLock 获取另一个次要代码。

我不明白为什么我们需要在 PnP 请求处理程序中获取删除锁?据我了解,我们只需要为挂起的 irp 获取删除锁,并在此类 irp 完成时释放它。因此,Windows 人员可以为我们提供 IoWaitForExistingRemoveLocks 例程而不是 IoReleaseRemoveLockAndWait

对不起,如果有点乱,我就是想不通。谢谢。

【问题讨论】:

    标签: windows driver wdm plug-and-play


    【解决方案1】:

    之后操作系统将发送IRP_MN_QUERY_DEVICE_RELATIONS 请求 BusRelations。在这个请求处理程序中,我们将排除设备 阵列并将做另一项必要的工作以“断开”它与总线的连接, 我们还将在其设备扩展中设置RemovePending 标志。

    这里只需要从数组中排除设备并在其设备扩展中设置RemovePending 标志。但是做另一项必要的工作来“断开”它与总线的连接 - 仅在处理 IRP_MN_REMOVE_DEVICE 时才需要(在总线驱动程序对 IRP_MN_QUERY_DEVICE_RELATIONS 请求 BusRelations 的最新响应中未包含设备之后 - 或者换句话说- 当RemovePending在其设备扩展中标记时)

    设备变成后如何处理传入的IO请求 “删除待处理”,在操作系统发送 IRP_MN_REMOVE_DEVICE 请求之前。 我们是否应该检查RemovePending 标志并返回 STATUS_DEVICE_DOES_NOT_EXIST 还是照常进行?

    我认为这两种行为都可能 - 您可以照常处理它,也可以返回 STATUS_DEVICE_DOES_NOT_EXIST。并假设下一种情况 - 您在移除设备过程中同时收到一些 IO 请求。当您检查 RemovePending 标志时 - 它尚未设置。然后您开始“照常”处理请求。但是在您检查 IO 请求中的RemovePending 标志之后,您可以在使用BusRelations 处理IRP_MN_QUERY_DEVICE_RELATIONS 请求时设置它。这种情况与使用Remove Locks Run-Down Protection的感觉直接相关。

    我们确实可以使用 Run-Down Protection 来代替 Remove Locks,其方式几乎与 Remove Locks 完全相同,并且在相同的位置和原因与 Remove Locks 相同。我认为 Run-Down Protection api(更多新的比较删除锁) - 更好的设计和更好的使用(但差异很小)

    我不明白为什么我们需要在 PnP 中获取删除锁 请求处理程序?

    首先请注意,关于删除锁仅在Removing a Device in a Function Driver 中说过。你我怎么理解没有功能,但是总线驱动程序-所以Removing a Device in a Bus Driver更适合你。在 Removing a Device in a Function Driver 的文档中存在 严重错误 - 建议首先在 4 - 之前8 点调用 IoReleaseRemoveLockAndWait strong> - 将IRP_MN_REMOVE_DEVICE 请求传递给下一个驱动程序。但正确的必须是

    驱动程序应该调用IoReleaseRemoveLockAndWait 之后它通过 IRP_MN_REMOVE_DEVICE 对下一个较低驱动程序的请求,以及 在它释放内存之前,调用IoDetachDevice,或者调用 IoDeleteDevice.

    这是正确的,并在Using Remove LocksIoReleaseRemoveLockAndWait 中说明。有趣的是,在旧的 msdn 版本中是

    调用IoReleaseRemoveLockAndWait它通过之前..

    但是现在这个问题已经解决了。为什么之后?因为下一个较低的驱动程序可以挂起一些 IRP(我们称之为 IoAcquireRemoveLockExAcquireRundownProtection)并仅在获得 IRP_MN_REMOVE_DEVICE 时完成它,而我们的驱动程序仅在此 IRP 完成时才调用 IoReleaseRemoveLockExReleaseRundownProtection。结果,如果调用 IoReleaseRemoveLockAndWaitExWaitForRundownProtectionRelease before 将删除 IRP 传递给下一个较低的驱动程序 - 我们可以在这里永远等待 - 下一个较低的驱动程序无法完成某些 IRP(直到没有被删除请求)并且我们不会释放删除锁定或中断保护。

    所以我们需要移除锁或损坏保护?因为我们可以让IRP_MN_REMOVE_DEVICE 与另一个 IO 请求同时发生。并且这个 IO 请求可以使用设备上的一些资源。当我们处理 IRP_MN_REMOVE_DEVICE 时,我们会从另一个大小中销毁这些资源。如果在IRP_MN_REMOVE_DEVICE 中销毁他之后,我们将在 IO 请求中使用一些资源会怎样?认为不需要回答。为了防止这种情况并存在移除锁或损坏保护。在使用任何资源(将在删除中销毁)之前,需要调用 IoAcquireRemoveLockExAcquireRundownProtection 并仅在返回 ok 状态时使用它。在我们完成使用资源调用IoReleaseRemoveLockExReleaseRundownProtection 之后。在IRP_MN_REMOVE_DEVICE 中,我们调用IoReleaseRemoveLockAndWaitExWaitForRundownProtectionRelease。在此调用返回后 - 我们可以确定没有人使用我们的资源并且永远不会被更多使用(对 IoAcquireRemoveLockExAcquireRundownProtection 的调用返回错误状态 (false))。此时我们可以安全地开始销毁资源:释放内存(等),调用IoDetachDeviceIoDeleteDevice

    指向下一个较低设备的指针 - 这也是资源,我们在处理 IO 请求中使用并在IRP_MN_REMOVE_DEVICE销毁(通过调用IoDetachDevice )。真的是正确的调用IofCallDriver(_nextDeviceObject, Irp);(同时处理一些IO请求)之后调用IoDetachDevice(_nextDeviceObject);(内部IRP_MN_REMOVE_DEVICE)?因为这个删除锁(或者我在这里使用自己的损坏保护)总是在函数和过滤驱动程序中使用。对于总线驱动程序,我们通常没有指向下一个较低设备的指针(PDO 未连接到另一个设备,当 FDO 连接到 PDO并且过滤器总是附在某物上) - 可能不需要锁(或根本不需要保护)。这取决于 - 是否存在其他资源 - 使用和销毁(移除时)。

    对于总线设备-通常情况下我们得到IRP_MN_REMOVE_DEVICE 2 次-首先在设备标记为RemovePending之前-所以我们转到第4点。在设备标记为RemovePending之后(因此设备不包括在总线驱动程序对 BusRelations 的 IRP_MN_QUERY_DEVICE_RELATIONS 请求的最新响应)我们最终销毁资源并调用 IoDeleteDevice

    【讨论】:

    • @RbMm谢谢你的回答,很清楚。所以这个想法是在我们处理另一个请求时保护资源(设备对象、它的扩展等)不被释放。我仍然不明白的事情:我们将删除锁定存储在设备扩展中,因此要获取它,我们需要访问此扩展。在我们访问它时,对 IoDeleteDevice 的调用尚未执行(因此设备对象及其扩展被释放)的保证在哪里?
    • @new_user - 如果有人向我们发送 IO 请求 - 他必须有指向 DeviceObject 的指针,并且必须确保他引用了它 (DeviceObject)。所以 DeviceObject 和 DeviceExtension 是有效的(作为内存存储)。但是 DeviceObject 的状态 - 已经是另一个问题 - 他对对象的引用 - 不能保证它的状态。仅记忆有效性。和用于保护对象的部分可被销毁之前对象本身将被破坏的破旧保护。
    • @new_user - 例如我在启动设备上分配内存缓冲区devext->Buf。和 io 请求使用了这个Buf。有人有指向我的设备(甚至是在其上打开的文件)的指针并将 ioctl 发送给它。设备对象本身将是有效的(作为内存存储),直到发送者不释放自己的引用(关闭文件)。即使我们调用IoDeleteDevice(最后一次引用变为0后内存将被释放)。但是如果我们在这个 ioctl 中同时收到删除请求并且不会使用损坏保护 - 我们可以释放 Buf 直到 ioctl 使用它。
    • 接下来调用ExWaitForRundownProtectionRelease - 我们等待此时是否有人使用Buf。并立即禁用更多使用Buf。在此之后我们可以安全免费Buf
    • @RbMm太好了,谢谢。对不起,如果我太好奇了,我还有一个问题。我有 DispatchDeviceControl、DispatchCleanup、DispatchClose 处理程序。这些请求是从用户态发送到总线 FDO 的,也就是说我们在总线 FDO 上打开了文件(因此总线 FDO 的有效性及其作为内存存储的扩展是有保证的),但是我们需要访问子 PDO 来完成请求(我们使用设备命名空间的概念): ChildPDO = (DEVICE_OBJECT*)((IoGetCurrentIrpStackLocation(Irp))->FileObject->FsContext);
    猜你喜欢
    • 1970-01-01
    • 2019-02-28
    • 2016-01-11
    • 1970-01-01
    • 1970-01-01
    • 2023-03-28
    • 1970-01-01
    • 1970-01-01
    • 2013-11-12
    相关资源
    最近更新 更多