在一个调度例程中,我们具有以下代码:

if (DeviceExtension->Flag)
{
    KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceExtension->SpinLock, &LockHandle);

    //... when we will enter here, DeviceExtension->Flag can already be set to FALSE.

    KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);
}


在另一个调度例程中,我们具有以下代码:

KeAcquireInStackQueuedSpinLockAtDpcLevel(&DeviceExtension->SpinLock, &LockHandle);

//...

DeviceExtension->Flag = FALSE;
KeReleaseInStackQueuedSpinLockFromDpcLevel(&LockHandle);


因此,当我们在第一个调度例程中获取自旋锁时,第二个例程已经可以将DeviceExtension->Flag设置为FALSE了。解决方案是获取自旋锁,然后检查DeviceExtension->Flag。但是DeviceExtension->Flag可能为FALSE,在这种情况下自旋锁获取似乎非常繁琐。

我对多线程不是很熟悉,尤其是在内核模式下。我知道这个问题很愚蠢,但我迷路了。在这种情况下正确的解决方案是什么?谢谢。

最佳答案

此标志指示要删除设备,因此它以一种方式工作


对于这个特殊存在Run-Down Protection

您需要在设备扩展名中包含EX_RUNDOWN_REF RunRef;成员,而不是bool Flag

用初始化

ExInitializeRundownProtection(&RunRef);


当您需要执行某些操作时,仅当尚未卸下设备时,才需要执行以下操作:

if (ExAcquireRundownProtection(&DeviceExtension->RunRef))
{
    // do something
    ExReleaseRundownProtection(&DeviceExtension->RunRef)
}


IRP_MN_REMOVE_DEVICE处理程序中,您需要调用

ExWaitForRundownProtectionRelease(&DeviceExtension->RunRef);




重要说明-尽管在msdn中指出必须在ExAcquireRundownProtection上调用ExReleaseRundownProtectionIRQL <= APC_LEVEL,但这是错误的。 ExAcquireRundownProtection只需对内存进行一些互锁的操作,直到RunRef-因此,如果它在非页面缓冲池中-我们可以在任何IRQL处调用此例程。设备扩展位于非页面缓冲池中。 ExReleaseRundownProtection可以将“等待”中的其他呼叫KeSetEvent设置为FALSE。结果,它可以在IRQL <= DISPATCH_LEVEL上运行。 ExReleaseRundownProtection我们通常从IoCompletion例程调用(该例程以小于或等于IRQLDISPATCH_LEVEL执行),因此在这里一切正常。

当然必须在ExWaitForRundownProtectionRelease处调用<= APC_LEVEL,因为在这里我们可以等待,但是PnP管理器在IRP_MN_REMOVE_DEVICE IRQL处发送PASSIVE_LEVEL-所以在这里一切都还可以



当然,您的此处可以使用Remove Locks和,它们与停机保护几乎完全相同。只需运行保护-更多新api,以及设计/实现更好的比较删除锁。但是在IoReleaseRemoveLockIoReleaseRemoveLock的文档中正确指出IRQL<= DISPATCH_LEVEL必须是IoReleaseRemoveLockAndWait必须在PASSIVE_LEVEL处调用

07-27 23:30