如其名所示,互斥體物件是一種同步機制,旨在確保核心模式執行緒之間共用的單一資源能被互斥地存取。 只有使用執行背景工作線程的文件系統驅動程式(FSD)等最高層級驅動程式可能會使用 mutex 物件。
可能,具有驅動程式建立線程或背景工作線程回呼例程的最高層級驅動程式可能會使用 Mutex 物件。 不過,任何具有可分頁執行緒或工作執行緒回呼例行程序的驅動程式,都必須謹慎地管理其 mutex 物件的取得、等候和釋放。
Mutex 物件具有內建功能,可讓系統(僅限內核模式)執行緒在 SMP(對稱多處理)機器中,以互斥且無死鎖的方式存取共用資源。 核心會一次將 Mutex 的擁有權指派給單一線程。
取得 Mutex 的擁有權可以防止通常的核心模式非同步程序調用(APC)的傳遞。 除非核心發出 APC_LEVEL 軟體中斷以執行特殊核心 APC,例如 I/O 管理員的 IRP 完成程序,將結果返回給 I/O 操作的原請求者,否則執行緒不會被先占用。
線程可以取得它已經擁有的 Mutex 物件的擁有權(遞歸擁有權),但遞歸取得的 Mutex 物件不會設定為 Signaled 狀態,直到線程完全釋放其擁有權為止。 這類線程必須在取得 Mutex 擁有權的每次之後明確釋放,才能讓另一個線程取得 Mutex。
內核絕不允許持有 Mutex 的線程在不先釋放 Mutex 並將其設定為信號狀態的情況下,切換至使用者模式。 如果由 FSD 建立或驅動程式建立且擁有 Mutex 的任何線程嘗試在釋放 Mutex 擁有權之前將控制權傳回給 I/O 管理員,核心就會使系統停機。
任何使用 mutex 對象的驅動程式都必須在等候或釋放其 Mutex 物件之前,先呼叫 KeInitializeMutex 一次。 下圖說明兩個系統線程如何使用 Mutex 物件。
如上圖所示,使用 mutex 物件的驅動程式必須提供 mutex 物件的記憶體,該對象必須是常駐。 驅動程式可以使用由其建立的裝置物件的裝置擴充、控制器物件的控制器擴充(如果使用控制器物件),或者驅動程式配置的非分頁集區。
當驅動程式呼叫 KeInitializeMutex(通常從 其 AddDevice 例行程序中),它必須傳遞指向用於儲存互斥體(Mutex)物件的記憶體位置的指標,核心會將其初始化為 Signaled 狀態。
在初始化如此高階驅動程序之後,它可以管理共用資源的互斥存取,如上圖所示。 例如,驅動程式的調度例程對於本質上同步的操作和執行緒,可能使用互斥鎖來保護針對 IRP 建立的驅動程式佇列。
因為 KeInitializeMutex一律會將 Mutex 物件的初始狀態設定為 Signaled (如上圖所示):
分派例程的初始呼叫 KeWaitForSingleObject 與 Mutex 指標會將目前的線程立即置於就緒狀態、提供 Mutex 的線程擁有權,並將 Mutex 狀態重設為 Not-Signaled。 一旦分派例程繼續執行,它便可以安全地將 IRP 插入由互斥體保護的佇列中。
當第二個線程(另一個分派例程、驅動程式提供的背景工作線程回呼例程或驅動程式建立線程)使用 Mutex 指標呼叫 KeWaitForSingleObject 時,第二個線程會進入等候狀態。
當分派例程如步驟 1 所述完成佇列 IRP 時,它會使用 Mutex 指標和 Boolean Wait 值呼叫 KeReleaseMutex,這表示它是否打算在 KeReleaseMutex 傳回控制後立即使用 Mutex 呼叫 KeWaitForSingleObject 或 KeWaitForMutexObject。
假設分派例程在步驟 3 中釋放了 Mutex 的擁有權(Wait 設為 FALSE),KeReleaseMutex 將 Mutex 設定為 Signaled 狀態。 Mutex 目前沒有擁有者,因此核心會判斷另一個線程是否正在等候該 Mutex。 如果是這樣,內核會將第二個執行緒(請參閱步驟 2)設為互斥鎖的擁有者,並可能將該執行緒的優先順序提升到最低的即時優先順序值,並將其狀態變更為可執行。
核心會在處理器可用時立即分派第二個線程來執行:也就是說,當沒有其他優先順序較高的線程處於就緒狀態,而且沒有核心模式例程可在較高的 IRQL 執行時。 第二執行緒(將 IRP 加入佇列的分派例程、驅動程式的工作執行緒回呼例程或驅動程式建立的執行緒將 IRP 移出佇列)現在可以安全地存取受 mutex 保護的 IRP 佇列,直到呼叫 KeReleaseMutex 為止。
如果線程以遞歸方式取得 mutex 物件的擁有權,該線程必須明確地呼叫 KeReleaseMutex,次數與其在 mutex 上等待的次數相同,才能將 mutex 物件設定為 Signaled 狀態。 例如,如果線程呼叫 KeWaitForSingleObject,然後使用相同的 Mutex 指標呼叫 KeWaitForMutexObject,它就必須在取得 Mutex 時呼叫 KeReleaseMutex 兩次,才能將該 Mutex 物件設定為 Signaled 狀態。
呼叫 KeReleaseMutex 並將 Wait 參數設定為 TRUE,表示呼叫端打算在 KeReleaseMutex 傳回時立即呼叫 KeWaitXxx 支援例程。
請考慮下列指導方針,將 Wait 參數設定為 KeReleaseMutex:
在 IRQL PASSIVE_LEVEL執行的可分頁線程或可分頁驅動程式例程,絕對不應該呼叫 KeReleaseMutex ,並將 Wait 參數設定為 TRUE。 如果呼叫端碰巧在 呼叫 KeReleaseMutex 和 KeWaitXxx對象之間分頁,就會造成嚴重的頁面錯誤。
任何在比 PASSIVE_LEVEL 更高的 IRQL 執行的標準驅動程式例程,不能在任何排程器物件上等待非零間隔,否則會導致系統崩潰。 不過,如果這類例程在 IRQL 小於或等於 DISPATCH_LEVEL 的情況下執行且持有 mutex,就可以呼叫 KeReleaseMutex。
如需標準驅動程式例程執行之 IRQL 的摘要,請參閱 管理硬體優先順序。