ミューテックス オブジェクトの概要
その名前が示すように、ミューテックス オブジェクトは、一連のカーネル モード スレッド間で共有される 1 つのリソースへの相互排他アクセスを保証するように設計された同期メカニズムです。 ミューテックス オブジェクトを使用する可能性が唯一あるのは、エグゼクティブ ワーカー スレッドを使用するファイル システム ドライバー (FSD) など、最上位レベルのドライバーです。
場合によっては、ドライバーによって作成されたスレッドまたはワーカー スレッド コールバック ルーチンを持つ最上位レベルのドライバーが、ミューテックス オブジェクトを使用する可能性があります。 ただし、ページング可能なスレッドまたはワーカー スレッド コールバック ルーチンを持つドライバーは、ミューテックス オブジェクトの取得、待機、リリースの管理をを非常に慎重に行う必要があります。
ミューテックス オブジェクトには、システム (カーネル モードのみ) スレッドに、SMP マシン内の共有リソースへの、相互に排他的でデッドロックを起こさないアクセスを提供する組み込み機能があります。 カーネルは、ミューテックスの所有権を一度に 1 つのスレッドに割り当てます。
ミューテックスの所有権を取得すると、通常のカーネル モードの非同期プロシージャ呼び出し (APC) を配信できなくなります。 カーネルが、APC_LEVEL ソフトウェア割り込みを発行して、I/O 操作の元の要求元に結果を返す I/O マネージャーの IRP 完了ルーチンなど、特別なカーネル APC を実行しない限り、スレッドが APC に割り込まれることはありません。
スレッドは既に所有しているミューテックス オブジェクトの所有権を取得できますが (再帰的な所有権)、再帰的に取得されたミューテックス オブジェクトは、スレッドがその所有権を完全に解放するまで Signaled 状態に設定されません。 このようなスレッドは、別のスレッドがミューテックスを取得する前に、所有権を取得した回数だけ明示的にミューテックスを解放する必要があります。
カーネルは、ミューテックスを所有するスレッドが、最初にミューテックスを解放して Signaled 状態に設定することなく、ユーザー モードへ遷移することを決して許可しません。 ミューテックスを所有する FSD によって作成されたスレッドまたはドライバーが作成したスレッドが、ミューテックスの所有権を解放する前に I/O マネージャーに制御を返そうとすると、カーネルによってシステムが停止します。
ミューテックス オブジェクトを使用するドライバーは、そのミューテックス オブジェクトを待機または解放する前に、KeInitializeMutex を 1 回呼び出す必要があります。 次の図は、2 つのシステム スレッドがミューテックス オブジェクトを使用する方法を示しています。
上の図に示すように、ミューテックス オブジェクトを使用するドライバーはミューテックス オブジェクトのためのストレージを提供する必要があります。これは常駐している必要があります。 ドライバーは、ドライバーが作成したデバイス オブジェクトのデバイス拡張、コントローラー拡張 (コントローラー オブジェクトを使用する場合)、またはドライバーによって割り当てられる非ページ プールを使用できます。
ドライバーは、KeInitializeMutexを呼び出すとき (通常は AddDevice ルーチンから)、カーネルが Signaled 状態に初期化するミューテックス オブジェクトのドライバーのストレージへのポインターを渡す必要があります。
このような最上位レベルのドライバーが初期化されると、上の図に示すように、共有リソースへの相互排他アクセスをドライバーは管理できるようになります。 たとえば、本質的に同期的な操作やスレッドのためのドライバーのディスパッチ ルーチンは、IRP 用にドライバーが作成したキューを保護するためにミューテックスを使用する場合があります。
KeInitializeMutex は常にミューテックス オブジェクトの初期状態を (前の図に示すように) Signaled に設定するため、次の手順に従って動作します。
ミューテックス ポインターを使用した KeWaitForSingleObject に対するディスパッチ ルーチンの最初の呼び出しは、現在のスレッドをすぐに準備完了状態にし、ミューテックスのスレッド所有権を与えて、ミューテックスの状態を Not-Signaled にリセットします。 ディスパッチ ルーチンの実行が再開されるとすぐに、ミューテックスで保護されたキューに IRP を安全に挿入できるようになります。
2 番目のスレッド (別のディスパッチ ルーチン、ドライバー指定のワーカー スレッド コールバック ルーチン、またはドライバーが作成したスレッド) がミューテックス ポインターを使用して KeWaitForSingleObject を呼び出すと、2 番目のスレッドは待機状態になります。
ディスパッチ ルーチンは、手順 1 で説明されているように IRP のキューを終了すると、Mutex ポインターと、KeReleaseMutex が制御を返すとすぐに Mutex で KeWaitForSingleObject (または KeWaitForMutexObject) を呼び出すかどうかを示すブール値 Wait を使用して、KeReleaseMutex を呼び出します。
ディスパッチ ルーチンが手順 3 でミューテックスの所有権を解放したこと (Wait の設定が FALSE) を仮定して、ミューテックスは KeReleaseMutex によって Signaled 状態に設定されます。 ミューテックスには現在所有者がないため、カーネルは別のスレッドがそのミューテックスを待機しているかどうかを特定します。 その場合、カーネルは 2 番目のスレッド (手順 2 を参照) をミューテックス所有者にし、場合により、スレッドの優先度を最も低いリアルタイムの優先度の値に昇格させ、状態を準備完了状態に変更します。
カーネルは、優先順位の高いスレッドが待機状態ではなく、より高い IRQL で実行すべきカーネル モード ルーチンが存在しない場合にのみ、プロセッサが使用可能になるとすぐに実行のために 2 番目のスレッドをディスパッチします。 2 番目のスレッド (IRP またはドライバーのワーカー スレッド コールバック ルーチンをキューに配置するディスパッチ ルーチン、または IRP をデキューするドライバーによって作成されたスレッド) は、これで、KeReleaseMutex を呼び出すまで IRP のミューテックスで保護されたキューに安全にアクセスできるようになりました。
スレッドがミューテックス オブジェクトの所有権を再帰的に取得する場合、そのスレッドは、ミューテックス オブジェクトを Signaled 状態に設定するために、ミューテックスを待機した回数だけ KeReleaseMutex を明示的に呼び出す必要があります。 たとえば、スレッドが同じ Mutex ポインターで KeWaitForSingleObject を呼び出した後、KeWaitForMutexObject を呼び出す場合、ミューテックス オブジェクトを Signaled 状態に設定するには、ミューテックスを取得するときに KeReleaseMutex を 2 回呼び出す必要があります。
Wait パラメーターを TRUE に設定して KeReleaseMutex を呼び出すことは、KeReleaseMutex からの戻り時にすぐに KeWaitXxx サポート ルーチンを呼び出す意図が、呼び出し元にあることを示します。
Wait パラメーターを KeReleaseMutex に設定するには、次のガイドラインを考慮してください。
IRQL PASSIVE_LEVEL で実行されるページング可能なスレッドまたはページング可能なドライバー ルーチンは、Wait パラメーターを TRUE に設定した状態で KeReleaseMutex を呼び出さないようにしてください。 このような呼び出しは、呼び出し元が KeReleaseMutex と KeWaitXxxObject の呼び出しの間にページ アウトされた場合に、致命的なページ フォールトが生じる原因となります。
PASSIVE_LEVEL より大きい IRQL で実行される標準ドライバー ルーチンは、システムを停止せずに、ディスパッチャー オブジェクトで 0 以外の間隔を待機することはできません。 ただし、このようなルーチンは、DISPATCH_LEVEL 以下の IRQL での実行中にミューテックスを所有している場合は、KeReleaseMutex を呼び出すことができます。
標準ドライバー ルーチンを実行する IRQL の概要については、「ハードウェアの優先度の管理」を参照してください。