クリティカル セクションの表示
クリティカル セクションは、さまざまな方法を使用してユーザー モードで表示できます。 各フィールドの正確な意味は、使用している Microsoft Windows バージョンによって異なります。
クリティカル セクションの表示
クリティカル セクションは、!ntsdexts.locks 拡張機能、!critsec 拡張機能、!cs 拡張機能、dt (タイプの表示) コマンドを使用して表示できます。
!ntsdexts.locks 拡張機能では、現在のプロセスに関連付けられているクリティカル セクションの一覧が表示されます。 -v オプションを使用すると、すべてのクリティカル セクションが表示されます。 例を次に示します。
0:000> !locks
CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount 0
RecursionCount 1
OwningThread c78
EntryCount 0
ContentionCount 0
*** Locked
....
Scanned 37 critical sections
表示するクリティカル セクションのアドレスがわかっている場合は、!critsec 拡張機能を使用できます。 これにより、!ntsdexts.locks と同じ情報のコレクションが表示されます。 次に例を示します。
0:000> !critsec 77fc49e0
CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount 0
RecursionCount 1
OwningThread c78
EntryCount 0
ContentionCount 0
*** Locked
!cs 拡張機能では、そのアドレスに基づいてクリティカル セクションを表示したり、クリティカル セクションのアドレス範囲を検索したりできます。また、各クリティカル セクションに関連付けられているスタック トレースを表示することもできます。 これらの機能の一部は、完全な Windows シンボルが正常に動作することを必要とします。 アプリケーション検証ツールがアクティブな場合は、!cs -t を使用してクリティカル セクション ツリーを表示できます。 詳細と例については、!cs のリファレンス ページを参照してください。
!cs によって表示される情報は、!ntsdexts.locks および !critsec によって表示される情報とは少し異なります。 次に例を示します。
## 0:000> !cs 77fc49e0
Critical section = 0x77fc49e0 (ntdll!FastPebLock+0x0)
DebugInfo = 0x77fc3e00
LOCKED
LockCount = 0x0
OwningThread = 0x00000c78
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x00000000
dt (タイプの表示) コマンドを使用すると、RTL_CRITICAL_SECTION 構造体のリテラル コンテンツを表示できます。 次に例を示します。
0:000> dt RTL_CRITICAL_SECTION 77fc49e0
+0x000 DebugInfo : 0x77fc3e00
+0x004 LockCount : 0
+0x008 RecursionCount : 1
+0x00c OwningThread : 0x00000c78
+0x010 LockSemaphore : (null)
+0x014 SpinCount : 0
Windows XP および Windows 2000 でのクリティカル セクション フィールドの解釈
クリティカル セクション構造の最も重要なフィールドは次のとおりです。
Microsoft Windows 2000 および Windows XP では、LockCount フィールドは、スレッドがこのクリティカル セクションの EnterCriticalSection ルーチンを呼び出した回数から 1 を引いた数を示します。 ロック解除されたクリティカル セクションの場合、このフィールドは -1 から始まります。 EnterCriticalSection を呼び出すたびに、この値が 1 だけ増えます。LeaveCriticalSection を呼び出すたびに、この値が 1 だけ減ります。 たとえば、LockCount が 5 の場合、このクリティカル セクションはロックされており、1 つのスレッドがそれを取得し、さらに 5 つのスレッドがこのロックを待機しています。
RecursionCount フィールドは、所有しているスレッドがこのクリティカル セクションに対して EnterCriticalSection を呼び出した回数を示します。
EntryCount フィールドは、所有しているスレッド以外のスレッドがこのクリティカル セクションに対して EnterCriticalSection を呼び出した回数を示します。
新しく初期化されたクリティカル セクションは次のようになります。
0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount NOT LOCKED
RecursionCount 0
OwningThread 0
EntryCount 0
ContentionCount 0
デバッガーには、LockCount の値として "NOT LOCKED" が表示されます。 ロック解除されたクリティカル セクションの場合、このフィールドの実際の値は -1 です。 これは、dt (タイプの表示) コマンドを使用して確認できます。
0:000> dt RTL_CRITICAL_SECTION 433e60
+0x000 DebugInfo : 0x77fcec80
+0x004 LockCount : -1
+0x008 RecursionCount : 0
+0x00c OwningThread : (null)
+0x010 LockSemaphore : (null)
+0x014 SpinCount : 0
最初のスレッドが EnterCriticalSection ルーチンを呼び出すと、クリティカル セクションの LockCount、RecursionCount、EntryCount、ContentionCount の各フィールドがすべて 1 ずつ増やされ、OwningThread が呼び出し元のスレッド ID になります。 EntryCount と ContentionCount が減らされることはありません。 次に例を示します。
0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount 0
RecursionCount 1
OwningThread 4d0
EntryCount 0
ContentionCount 0
この時点で、4 つの異なる処理が行われる可能性があります。
所有しているスレッドが EnterCriticalSection をもう一度呼び出します。 これにより、LockCount と RecursionCount が 1 だけ増やされます。 EntryCount は増やされません。
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount 1 RecursionCount 2 OwningThread 4d0 EntryCount 0 ContentionCount 0
別のスレッドが EnterCriticalSection を呼び出します。 これにより、LockCount と EntryCount が 1 だけ増やされます。 RecursionCount は増やされません。
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount 1 RecursionCount 1 OwningThread 4d0 EntryCount 1 ContentionCount 1
所有しているスレッドが LeaveCriticalSection を呼び出します。 これにより、LockCount と RecursionCount が 1 ずつ減らされて、それぞれ -1 と 0 になり、OwningThread が 0 にリセットされます。
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount NOT LOCKED RecursionCount 0 OwningThread 0 EntryCount 0 ContentionCount 0
別のスレッドが LeaveCriticalSection を呼び出します。 これにより、LeaveCriticalSection を呼び出す所有スレッドと同じ結果が生成されます。LockCount と RecursionCount が 1 ずつ減らされて、それぞれ -1 と 0 になり、OwningThread が 0 にリセットされます。
スレッドが LeaveCriticalSection を呼び出すと、Windows は LockCount と RecursionCount を 1 ずつ減らします。 この機能には、良い面と悪い面の両方があります。 デバイス ドライバーは、1 つのスレッドでクリティカル セクションに入り、別のスレッドでクリティカル セクションを離れることができます。 ただし、誤って間違ったスレッドで LeaveCriticalSection を呼び出したり、LeaveCriticalSection を何度も呼び出したために LockCount が -1 より小さい値になったりする可能性もあります。 これにより、クリティカル セクションが破損し、すべてのスレッドがクリティカル セクションで無期限に待機する原因となります。
Windows Server 2003 SP1 以降でのクリティカル セクション フィールドの解釈
Microsoft Windows Server 2003 Service Pack 1 以降のバージョンの Windows では、LockCount フィールドは次のように解析されます。
最下位ビットはロック状態を示します。 このビットが 0 の場合、クリティカル セクションはロックされています。1 の場合、クリティカル セクションはロックされていません。
次のビットは、スレッドがこのロックに対して開始されたかどうかを示します。 このビットが 0 の場合、このロックに対してスレッドが開始されています。1 の場合、スレッドは開始されていません。
残りのビットは、ロックを待機しているスレッド数の 1 の補数です。
たとえば、LockCount が -22 であるとします。 最下位ビットは次のように決定できます。
0:009> ? 0x1 & (-0n22)
Evaluate expression: 0 = 00000000
次に下位のビットは、次のように決定できます。
0:009> ? (0x2 & (-0n22)) >> 1
Evaluate expression: 1 = 00000001
残りのビットの 1 の補数は、次のように決定できます。
0:009> ? ((-1) - (-0n22)) >> 2
Evaluate expression: 5 = 00000005
この例では、最初のビットが 0 であるため、クリティカル セクションはロックされています。 2 番目のビットは 1 であるため、このロックに対してスレッドは開始されていません。 残りのビットの補数は 5 であるため、このロックを待機しているスレッドは 5 つあります。
追加情報
クリティカル セクションのタイムアウトをデバッグする方法については、「クリティカル セクションのタイムアウト」を参照してください。 クリティカル セクションの全般情報については、Microsoft Windows SDK のドキュメント、Windows Driver Kit (WDK) のドキュメント、または Mark Russinovich および David Solomon による Microsoft Windows Internals を参照してください。