Отображение критического раздела
Критические разделы могут отображаться в пользовательском режиме различными способами. Точное значение каждого поля зависит от используемой версии 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 для разблокированного критического раздела. Каждый вызов 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 (тип отображения):
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 становится идентификатором вызывающего объекта. 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 с пакетом обновления 1 (SP1) и более поздних версиях
В Microsoft Windows Server 2003 с пакетом обновления 1 (SP1) и более поздних версиях 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
1-дополнение оставшихся битов можно определить следующим образом:
0:009> ? ((-1) - (-0n22)) >> 2
Evaluate expression: 5 = 00000005
В этом примере первый бит равен 0, поэтому критически важный раздел заблокирован. Второй бит равен 1, поэтому ни один поток не был просочен для этой блокировки. В дополнение к остальным битам 5, поэтому есть пять потоков, ожидающих этой блокировки.
Дополнительные сведения
Сведения об отладке тайм-аутов критических разделов см. в разделе Time Outs critical Section. Общие сведения о критически важных разделах см. в Microsoft Windows SDK, комплекте драйверов Для Windows (WDK) или в Microsoft Windows Internals от Марка Руссиновича и Дэвида Соломона.