偵錯死結 - DRIVER_VERIFIER_DETECTED_VIOLATION (C4): 0x1001
當驅動程式驗證器偵測到微調鎖定階層違規時,驅動程式驗證器會產生錯誤檢查0xC4:DRIVER_VERIFIER_DETECTED_VIOLATION參數 1 值為 0x1001。
當死結偵測選項為作用中時(死結偵測是驅動程序驗證器標準選項的一部分), 驅動程序驗證器 會追蹤配置的每個微調鎖定,以及取得和釋放它的順序。 鎖定階層違規表示驅動程序驗證器偵測到至少一種情況,即在取得 LockB 之前取得並保留 LockA,而另一種情況則是在需要 LockA 之前取得並保留 LockB。
重要: 每當 驅動程序驗證器 偵測到階層違規發生時,就會進行此錯誤檢查,而不是發生實際死結的情況時。 此違規會強制強制執行任何在驅動程式的各種元件之間共用的鎖定,一律應以順序取得和釋放,使兩個線程的死結變得不可能。
Windows 8.1 的新功能:當 驅動程式驗證程式 遇到此違規時,如果附加調試程式,調試程式會要求您輸入有關錯誤的資訊。 在 Windows 8 和舊版 Windows 中,此違規會導致立即檢查錯誤。
************ Verifier Detected a Potential Deadlock *************
**
** Type !deadlock in the debugger for more information.
**
*****************************************************************
*** Verifier assertion failed ***
(B)reak, (I)gnore, (W)arn only, (R)emove assert?
若要在執行 Windows 8.1 的電腦上偵錯此違規,請選擇 B (中斷),然後輸入建議的調試程式命令 (!deadlock):
kd> !deadlock
issue: 00001001 97dd800c 86858ce0 00000000
Deadlock detected (2 locks in 2 threads):
Thread 0: A B.
Thread 1: B A.
Where:
Thread 0 = TERMINATED.
Thread 1 = c4ae2040.
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
!deadlock 3 命令也可以用來顯示詳細資訊,包括上次取得時堆棧:
kd> !deadlock 3
issue: 00001001 97dd800c 86858ce0 00000000
Deadlock detected (2 locks in 2 threads):
#
Thread 0: TERMINATED took locks in the following order:
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Node: 8685acd8
Stack: 97dd65b7 MyTestDriver!SystemControlIrpWorker+0x00000027
97dd605a MyTestDriver!Dispatch_SystemControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
Node: 86833578
Stack: 97dd65c5 MyTestDriver!SystemControlIrpWorker+0x00000a4a
97dd605a MyTestDriver!Dispatch_SystemControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
#
Thread 1: c4ae2040 (ThreadEntry = 86833a08) took locks in the following order:
Lock B = 97dd8008 (MyTestDriver!BravoLock+0x00000000) - Type 'Spinlock'.
Node: 86858ce0
Stack: 97dd65ef MyTestDriver!DeviceControlIrpWorker+0x0000005f
97dd605a MyTestDriver!Dispatch_DeviceControl+0x0000001a
820c4b4d nt!IovCallDriver+0x000002cc
81ca3772 nt!IofCallDriver+0x00000062
81eb165e nt!IopSynchronousServiceTail+0x0000016e
81eb5518 nt!IopXxxControlFile+0x000003e8
81eb4516 nt!NtDeviceIoControlFile+0x0000002a
81d27e17 nt!KiSystemServicePostCall+0x00000000
Lock A = 97dd800c (MyTestDriver!AlphaLock+0x00000000) - Type 'Spinlock'.
Stack: << Current stack trace - use kb to display it >>
調試程式會建議使用 kb (Display Stack Backtrace) 命令來顯示目前的堆疊追蹤。
kd> kb
ChildEBP RetAddr Args to Child
89b2cac4 820da328 97dd800c 86858ce0 00000000 nt!VfReportIssueWithOptions+0x86
89b2caf4 820d92a2 00000001 00000000 97dd65fd nt!ViDeadlockAnalyze+0x1d1
89b2cb7c 820d424e 86858ce0 00000000 97dd65fd nt!VfDeadlockAcquireResource+0x2fd
89b2cb9c 97dd65fd 00007780 89b2cbbc 97dd605a nt!VerifierKfAcquireSpinLock+0x8c
89b2cba8 97dd605a 9a9e7780 88d08f48 00000000 MyTestDriver!DeviceControlIrpWorker+0x54a
89b2cbbc 820c4b4d 9a9e7780 88d08f48 820c4881 MyTestDriver!Dispatch_DeviceControl+0x1a
(Inline) -------- -------- -------- -------- nt!IopfCallDriver+0x47
89b2cbe0 81ca3772 81eb165e b3c9ff80 88d08f48 nt!IovCallDriver+0x2cc
89b2cbf4 81eb165e 88d08fdc 88d08f48 00000000 nt!IofCallDriver+0x62
調試程式輸出顯示有問題的驅動程式在取得鎖定 B 之前先取得鎖定 A,再取得鎖定 B,並嘗試在另一個線程上取得鎖定 A 來違反此規則。 請注意,第一個線程 (Thread 0) 已終止,因此在載入驅動程式映像之後,這兩個鎖定的擷取和後續釋放會在某個時間點發生。
載入測試驅動程式的適當符號時,調試程式會顯示當時取得鎖定的函式。 在此範例中,鎖定 A 和 Lock B 都會在同一個函式中取得。 在 Thread 0 中,它們都是在 SystemControlIrpWorker 中取得。 在 Thread 1 中,這兩者都是在 DeviceControlIrpWorker 中取得的(如來自 !deadlock 3 的 Lock B 輸出和目前的堆棧輸出 #kb 所示。
此時,檢閱每個函式的原始碼應該會顯示程式代碼路徑存在,其中可以依此類順序取得鎖定。
MyTestDriver! AlphaLock 和 MyTestDriver!BravoLock 是驅動程式中全域可用的物件:
include "MyTestDriverHeader.h"
// Locks used to control access to various objects
extern KSPIN_LOCK AlphaLock;
extern KSPIN_LOCK BravoLock;
在 SystemControlIrpWorker 函式內部,有一個路徑,其中在取得 BravoLock (Lock B) 時,會取得並保留 AlphaLock (!死結輸出中的 Lock A)。 值得注意的是,鎖定會以取得鎖定的反向順序正確釋放。 (下列程式代碼會大量編輯,只顯示產生此案例所需的元素。
NTSTATUS SystemControlIrpWorker(_In_ PIRP Irp)
{
KIRQL IrqlAlpha;
KIRQL IrqlBravo;
// <<Other local variable declarations removed>>
// <<Various lines of code not shown>>
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
// <<Various lines of code not shown>>
KeAcquireSpinLock (&BravoLock, &IrqlBravo);
// <<Various lines of code not shown>>
KeReleaseSpinLock (&BravoLock, IrqlBravo);
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
// <<Various lines of code not shown>>
}
如果您檢閱下列 DeviceControlIrpWorker 範例函式,您可以看到可以反向取得鎖定。 也就是說,在嘗試收購 AlphaLock 時,可以取得並保留 BravoLock。 下列範例已簡化,但會顯示可能發生違規的路徑。
NTSTATUS DeviceControlIrpWorker(_In_ PIRP Irp,
_In_ BOOLEAN bSomeCondition)
{
KIRQL IrqlAlpha;
KIRQL IrqlBravo;
// <<Other local variable declarations removed>>
if (bSomeCondition == FALSE)
{
//
// Note that if bSomeCondition is FALSE, then AlphaLock is acquired here
// If bSomeCondition is TRUE, it is not needed to be acquired right now
//
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
}
// <<Various lines of code not shown>>
KeAcquireSpinLock (&BravoLock, &IrqlBravo);
// <<Various lines of code not shown>>
if (bSomeCondition == TRUE)
{
//
// Need to acquire AlphaLock here for upcoming code logic
//
KeAcquireSpinLock (&AlphaLock, &IrqlAlpha);
// <<Various lines of code not shown>>
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
}
// <<Various lines of code not shown>>
KeReleaseSpinLock (&BravoLock, IrqlBravo);
if (bSomeCondition == FALSE)
{
//
// Release the AlphaLock, which was acquired much earlier
//
KeReleaseSpinLock (&AlphaLock, IrqlAlpha);
}
// <<Various lines of code not shown>>
}
若要修正此潛在違規問題,正確的作法是確保每當司機嘗試取得AlphaLock時,就會檢查並確認未保留BravoLock。 最簡單的修正方式可能是只要釋放 BravoLock,並在取得 AlphaLock 後立即重新取得它。 但是,如果 BravoLock 保護的數據在等待 AlphaLock 和重新取得 BravoLock 時不會變更,則可能需要更顯著的程式碼變更。
如需微調鎖定和其他同步處理技術的詳細資訊,請參閱 微調鎖定。