Поделиться через


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

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

На этом этапе могут произойти четыре различные вещи.

  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. Это приведет к уменьшению значения 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
    
  4. Другой поток вызывает 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 от Марка Руссиновича и Дэвида Соломона.