Partilhar via


Exibindo uma seção crítica

Seções críticas podem ser exibidas no modo de usuário por uma variedade de métodos diferentes. O significado exato de cada campo depende da versão do Microsoft Windows que você está usando.

Exibindo seções críticas

Seções críticas podem ser exibidas pela extensão !ntsdexts.locks, a extensão !critsec, a extensão !cs e o comando dt (Tipo de Exibição).

A extensão !ntsdexts.locks exibe uma lista de seções críticas associadas ao processo atual. Se a opção -v for usada, todas as seções críticas serão exibidas. Veja um exemplo:

0:000> !locks

CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount          0
RecursionCount     1
OwningThread       c78
EntryCount         0
ContentionCount    0
*** Locked

....
Scanned 37 critical sections

Se você souber o endereço da seção crítica que deseja exibir, poderá usar a extensão !critsec . Isso exibe a mesma coleção de informações que !ntsdexts.locks. Por exemplo:

0:000> !critsec 77fc49e0

CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount          0
RecursionCount     1
OwningThread       c78
EntryCount         0
ContentionCount    0
*** Locked

A extensão !cs pode exibir uma seção crítica com base em seu endereço, pesquisar seções críticas em um intervalo de endereços e até mesmo exibir o rastreamento de pilha associado a cada seção crítica. Alguns desses recursos exigem símbolos completos do Windows para funcionar corretamente. Se o Verificador de Aplicativo estiver ativo, !cs -t poderá ser usado para exibir a árvore de seção crítica. Consulte a página de referência !cs para obter detalhes e exemplos.

As informações exibidas por !cs são ligeiramente diferentes das mostradas por !ntsdexts.locks e !critsec. Por exemplo:

## 0:000> !cs 77fc49e0

Critical section   = 0x77fc49e0 (ntdll!FastPebLock+0x0)
DebugInfo          = 0x77fc3e00
LOCKED
LockCount          = 0x0
OwningThread       = 0x00000c78
RecursionCount     = 0x1
LockSemaphore      = 0x0
SpinCount          = 0x00000000

O comando dt (Tipo de Exibição) pode ser usado para exibir o conteúdo literal da estrutura RTL_CRITICAL_SECTION. Por exemplo:

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

Interpretando campos de seção críticos no Windows XP e no Windows 2000

Os campos mais importantes da estrutura de seção crítica são os seguintes:

  • No Microsoft Windows 2000 e no Windows XP, o campo LockCount indica o número de vezes que qualquer thread chamou a rotina EnterCriticalSection para esta seção crítica, menos uma. Esse campo começa em -1 para uma seção crítica desbloqueada. Cada chamada de EnterCriticalSection incrementa esse valor; cada chamada de LeaveCriticalSection a diminui. Por exemplo, se LockCount for 5, essa seção crítica será bloqueada, um thread a adquirirá e cinco threads adicionais estarão aguardando esse bloqueio.

  • O campo RecursionCount indica o número de vezes que o thread proprietário chamou EnterCriticalSection para esta seção crítica.

  • O campo EntryCount indica o número de vezes que um thread diferente do thread proprietário chamou EnterCriticalSection para esta seção crítica.

Uma seção crítica recém-inicializada tem esta aparência:

0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount          NOT LOCKED 
RecursionCount     0
OwningThread       0
EntryCount         0
ContentionCount    0

O depurador exibe "NOT LOCKED" como o valor de LockCount. O valor real desse campo para uma seção crítica desbloqueada é -1. Você pode verificar isso com o comando dt (Tipo de Exibição):

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

Quando o primeiro thread chama a rotina EnterCriticalSection , os campos LockCount, RecursionCount, EntryCount e ContentionCount da seção crítica são incrementados por um e OwningThread se torna a ID do thread do chamador. EntryCount e ContentionCount nunca são decrementado. Por exemplo:

0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount          0
RecursionCount     1
OwningThread       4d0
EntryCount         0
ContentionCount    0

Neste ponto, quatro coisas diferentes podem acontecer.

  1. O thread proprietário chama EnterCriticalSection novamente. Isso incrementará LockCount e RecursionCount. EntryCount não é incrementado.

    0:000> !critsec 433e60
    CritSec mymodule!cs+0 at 00433E60
    LockCount          1
    RecursionCount     2
    OwningThread       4d0
    EntryCount         0
    ContentionCount    0
    
  2. Um thread diferente chama EnterCriticalSection. Isso incrementará LockCount e EntryCount. RecursionCount não foi incrementado.

    0:000> !critsec 433e60
    CritSec mymodule!cs+0 at 00433E60
    LockCount          1
    RecursionCount     1
    OwningThread       4d0
    EntryCount         1
    ContentionCount    1
    
  3. O thread proprietário chama LeaveCriticalSection. Isso diminuirá LockCount (para -1) e RecursionCount (para 0) e redefinirá OwningThread como 0.

    0:000> !critsec 433e60
    CritSec mymodule!cs+0 at 00433E60
    LockCount          NOT LOCKED 
    RecursionCount     0
    OwningThread       0
    EntryCount         0
    ContentionCount    0
    
  4. Outro thread chama LeaveCriticalSection. Isso produz os mesmos resultados que o thread proprietário que chama LeaveCriticalSection – ele diminuirá LockCount (para -1) e RecursionCount (para 0) e redefinirá OwningThread para 0.

Quando qualquer thread chama LeaveCriticalSection, o Windows diminui LockCount e RecursionCount. Esse recurso tem aspectos bons e ruins. Ele permite que um driver de dispositivo insira uma seção crítica em um thread e deixe a seção crítica em outro thread. No entanto, também torna possível chamar Acidentalmente LeaveCriticalSection no thread errado ou chamar LeaveCriticalSection muitas vezes e fazer com que LockCount atinja valores menores que -1. Isso corrompe a seção crítica e faz com que todos os threads aguardem indefinidamente na seção crítica.

Interpretando campos de seção críticos no Windows Server 2003 SP1 e posterior

No Microsoft Windows Server 2003 Service Pack 1 e versões posteriores do Windows, o campo LockCount é analisado da seguinte maneira:

  • O bit mais baixo mostra o status de bloqueio. Se esse bit for 0, a seção crítica será bloqueada; se for 1, a seção crítica não será bloqueada.

  • O próximo bit mostra se um thread foi acordado para esse bloqueio. Se esse bit for 0, um thread será acordado para esse bloqueio; se for 1, nenhum thread foi acordado.

  • Os bits restantes são o complemento ones do número de threads que aguardam o bloqueio.

Por exemplo, suponha que o LockCount seja -22. O bit mais baixo pode ser determinado dessa forma:

0:009> ? 0x1 & (-0n22)
Evaluate expression: 0 = 00000000

O próximo bit mais baixo pode ser determinado dessa forma:

0:009> ? (0x2 & (-0n22)) >> 1
Evaluate expression: 1 = 00000001

O complemento ones dos bits restantes pode ser determinado dessa forma:

0:009> ? ((-1) - (-0n22)) >> 2
Evaluate expression: 5 = 00000005

Neste exemplo, o primeiro bit é 0 e, portanto, a seção crítica está bloqueada. O segundo bit é 1 e, portanto, nenhum thread foi acordado para esse bloqueio. O complemento dos bits restantes é 5 e, portanto, há cinco threads aguardando esse bloqueio.

Informações adicionais

Para obter informações sobre como depurar tempos limite críticos da seção, consulte Tempos limite críticos da seção. Para obter informações gerais sobre seções críticas, consulte o SDK do Microsoft Windows, o WDK (Kit de Driver do Windows) ou Os Internos do Microsoft Windows de Mark Russinovich e David Solomon.