在一个调度例程中,我们具有以下代码:
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
上调用ExReleaseRundownProtection
和IRQL <= APC_LEVEL
,但这是错误的。 ExAcquireRundownProtection
只需对内存进行一些互锁的操作,直到RunRef
-因此,如果它在非页面缓冲池中-我们可以在任何IRQL
处调用此例程。设备扩展位于非页面缓冲池中。 ExReleaseRundownProtection
可以将“等待”中的其他呼叫KeSetEvent
设置为FALSE
。结果,它可以在IRQL <= DISPATCH_LEVEL
上运行。 ExReleaseRundownProtection
我们通常从IoCompletion
例程调用(该例程以小于或等于IRQL
的DISPATCH_LEVEL
执行),因此在这里一切正常。当然必须在
ExWaitForRundownProtectionRelease
处调用<= APC_LEVEL
,因为在这里我们可以等待,但是PnP管理器在IRP_MN_REMOVE_DEVICE
IRQL
处发送PASSIVE_LEVEL
-所以在这里一切都还可以当然,您的此处可以使用Remove Locks和,它们与停机保护几乎完全相同。只需运行保护-更多新api,以及设计/实现更好的比较删除锁。但是在
IoReleaseRemoveLock
和IoReleaseRemoveLock
的文档中正确指出IRQL<= DISPATCH_LEVEL
必须是IoReleaseRemoveLockAndWait
必须在PASSIVE_LEVEL
处调用