Отображение критического раздела

Критические разделы можно отображать в пользовательском режиме различными методами. Точное значение каждого поля зависит от используемой версии 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 указывает количество раз, когда собственный поток назвал ВводCriticalSection для этого критического раздела.

  • Поле 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

На этом этапе может произойти четыре разных события.

  1. Собственный поток снова вызывает EnterCriticalSection . Это увеличит LockCount и RecursionCount. EntryCount не увеличивается.

    0:000> !critsec 433e60
    CritSec mymodule!cs+0 at 00433E60
    LockCount          1
    RecursionCount     2
    OwningThread       4d0
    EntryCount         0
    ContentionCount    0
    
  2. Другой поток вызывает EnterCriticalSection. Это увеличит LockCount и EntryCount. RecursionCount не увеличивается.

    0:000> !critsec 433e60
    CritSec mymodule!cs+0 at 00433E60
    LockCount          1
    RecursionCount     1
    OwningThread       4d0
    EntryCount         1
    ContentionCount    1
    
  3. Собственный поток вызывает LeaveCriticalSection. Это приведет к уменьшению блокировки (до -1) и RecursionCount (до 0) и сбросит значение 0.

    0:000> !critsec 433e60
    CritSec mymodule!cs+0 at 00433E60
    LockCount          NOT LOCKED 
    RecursionCount     0
    OwningThread       0
    EntryCount         0
    ContentionCount    0
    
  4. Другой поток вызывает LeaveCriticalSection. Это даёт тот же результат, что и вызов LeaveCriticalSection: он уменьшит LockCount (до -1), RecursionCount (до 0), и сбросит OwningThread на 0.

Когда любой поток вызывает LeaveCriticalSection, Windows уменьшает LockCount и RecursionCount. Эта функция имеет как хорошие, так и плохие аспекты. Он позволяет драйверу устройства ввести критически важный раздел в одном потоке и оставить критически важный раздел в другом потоке. Однако он также позволяет случайно вызвать LeaveCriticalSection в неправильном потоке или вызвать LeaveCriticalSection слишком много раз и вызвать LockCount достичь значений ниже -1. Это повреждает критически важный раздел и приводит к тому, что все потоки будут ждать неограниченное время в критическом разделе.

Интерпретация полей критической секции в Windows Server 2003 SP1 и позже

В Microsoft Windows Server 2003 с пакетом обновления 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, и поэтому есть пять потоков, ожидающих этой блокировки.

дополнительные сведения

Для получения сведений о том, как отлаживать время ожидания в критических разделах, см. Времени ожидания в критических разделах. Общие сведения о критически важных разделах см. в пакете SDK для Microsoft Windows, комплекте драйверов Windows (WDK) или внутренних компонентах Microsoft Windows марком Руссиновичем и Дэвидом Соломоном.