显示关键节
关键部分可以通过各种不同的方法在用户模式下显示。 每个字段的确切含义取决于所使用的 Microsoft Windows 版本。
显示关键部分
关键部分可由 !ntsdexts.locks 扩展、 !critsec 扩展、 !cs 扩展和 dt (Display Type) 命令显示。
!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 (Display Type) 命令可用于显示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 开始。 每次调用 EnterCriticalSection 都会递增此值;每次调用 LeaveCriticalSection 都会递减它。 例如,如果 LockCount 为 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
调试器将“NOT LOCKED”显示为 LockCount 的值。 此字段对于已解锁的关键部分的实际值为 -1。 可以使用 dt (Display Type) 命令对此进行验证:
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 字段全部递增一个, 而 OwningThread 将成为调用方线程 ID。 EntryCount 和 ContentionCount 永远不会递减。 例如:
0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount 0
RecursionCount 1
OwningThread 4d0
EntryCount 0
ContentionCount 0
此时,可能会发生四个不同的事情。
拥有线程再次调用 EnterCriticalSection 。 这会递增 LockCount 和 RecursionCount。 EntryCount 不递增。
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount 1 RecursionCount 2 OwningThread 4d0 EntryCount 0 ContentionCount 0
另一个线程调用 EnterCriticalSection。 这会递增 LockCount 和 EntryCount。 RecursionCount 不递增。
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount 1 RecursionCount 1 OwningThread 4d0 EntryCount 1 ContentionCount 1
拥有的线程调用 LeaveCriticalSection。 这会将 LockCount (减为 -1) , 将 RecursionCount (减为 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 (减为 -1) ,recursionCount (减为 0) ,并将 OwningThread 重置为 0。
当任何线程调用 LeaveCriticalSection 时,Windows 会递减 LockCount 和 RecursionCount。 此功能具有好方面和坏方面。 它允许设备驱动程序在一个线程上进入关键部分,并将关键部分保留在另一个线程上。 但是,它也有可能在错误的线程上意外调用 LeaveCriticalSection ,或者调用 LeaveCriticalSection 太多次,导致 LockCount 达到低于 -1 的值。 这会损坏关键部分,并导致所有线程无限期地等待关键部分。
解释 Windows Server 2003 SP1 及更高版本中的关键节字段
在 Microsoft Windows Server 2003 Service Pack 1 及更高版本的 Windows 中, LockCount 字段分析如下:
最低位显示锁定状态。 如果此位为 0,则锁定关键部分;如果为 1,则不锁定关键部分。
下一位显示是否已为此锁唤醒线程。 如果此位为 0,则表示已为此锁唤醒线程;如果为 1,则表示尚未唤醒任何线程。
剩余的位是等待锁的线程数的补码。
例如,假设 LockCount 为 -22。 可按以下方式确定最低位:
0:009> ? 0x1 & (-0n22)
Evaluate expression: 0 = 00000000
可按以下方式确定下一个最低位:
0:009> ? (0x2 & (-0n22)) >> 1
Evaluate expression: 1 = 00000001
可以按以下方式确定剩余位的补数:
0:009> ? ((-1) - (-0n22)) >> 2
Evaluate expression: 5 = 00000005
在此示例中,第一个位为 0,因此关键部分被锁定。 第二个位为 1,因此尚未为此锁唤醒任何线程。 剩余位的补数为 5,因此有 5 个线程等待此锁。
其他信息
有关如何调试关键节超时的信息,请参阅 关键节超时。 有关关键部分的一般信息,请参阅 Microsoft Windows SDK、Windows 驱动程序工具包 (WDK) 或 Microsoft Windows Internals(由 Mark Russinovich 和 David Solomon 撰写)。