Affichage d’une section critique
Les sections critiques peuvent être affichées en mode utilisateur par différentes méthodes. La signification exacte de chaque champ dépend de la version de Microsoft Windows que vous utilisez.
Affichage des sections critiques
Les sections critiques peuvent être affichées par l’extension !ntsdexts.locks, l’extension !critsec, l’extension !cs et la commande dt (Type d’affichage).
L’extension !ntsdexts.locks affiche une liste de sections critiques associées au processus actuel. Si l’option -v est utilisée, toutes les sections critiques s’affichent. Voici un exemple :
0:000> !locks
CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount 0
RecursionCount 1
OwningThread c78
EntryCount 0
ContentionCount 0
*** Locked
....
Scanned 37 critical sections
Si vous connaissez l’adresse de la section critique que vous souhaitez afficher, vous pouvez utiliser l’extension !critsec . Cela affiche la même collection d’informations que !ntsdexts.locks. Par exemple :
0:000> !critsec 77fc49e0
CritSec ntdll!FastPebLock+0 at 77FC49E0
LockCount 0
RecursionCount 1
OwningThread c78
EntryCount 0
ContentionCount 0
*** Locked
L’extension !cs peut afficher une section critique en fonction de son adresse, rechercher des sections critiques dans une plage d’adresses et même afficher la trace de pile associée à chaque section critique. Certaines de ces fonctionnalités nécessitent des symboles Windows complets pour fonctionner correctement. Si Application Verifier est actif, !cs -t peut être utilisé pour afficher l’arborescence des sections critiques. Pour plus d’informations et des exemples, consultez la page de référence !cs .
Les informations affichées par !cs sont légèrement différentes de celles affichées par !ntsdexts.locks et !critsec. Par exemple :
## 0:000> !cs 77fc49e0
Critical section = 0x77fc49e0 (ntdll!FastPebLock+0x0)
DebugInfo = 0x77fc3e00
LOCKED
LockCount = 0x0
OwningThread = 0x00000c78
RecursionCount = 0x1
LockSemaphore = 0x0
SpinCount = 0x00000000
La commande dt (Type d’affichage) peut être utilisée pour afficher le contenu littéral de la structure RTL_CRITICAL_SECTION. Par exemple :
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
Interprétation de champs de section critiques dans Windows XP et Windows 2000
Les champs les plus importants de la structure de section critique sont les suivants :
Dans Microsoft Windows 2000 et Windows XP, le champ LockCount indique le nombre de fois où un thread a appelé la routine EnterCriticalSection pour cette section critique, moins une. Ce champ commence à -1 pour une section critique déverrouillée. Chaque appel d’EnterCriticalSection incrémente cette valeur ; chaque appel de LeaveCriticalSection le décrémente. Par exemple, si LockCount a la valeur 5, cette section critique est verrouillée, un thread l’a acquise et cinq threads supplémentaires attendent ce verrou.
Le champ RecursionCount indique le nombre de fois où le thread propriétaire a appelé EnterCriticalSection pour cette section critique.
Le champ EntryCount indique le nombre de fois où un thread autre que le thread propriétaire a appelé EnterCriticalSection pour cette section critique.
Une section critique nouvellement initialisée ressemble à ceci :
0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount NOT LOCKED
RecursionCount 0
OwningThread 0
EntryCount 0
ContentionCount 0
Le débogueur affiche « NOT LOCKED » comme valeur pour LockCount. La valeur réelle de ce champ pour une section critique déverrouillée est -1. Vous pouvez le vérifier avec la commande dt (Type d’affichage) :
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
Lorsque le premier thread appelle la routine EnterCriticalSection , les champs LockCount, RecursionCount, EntryCount et ContentionCount de la section critique sont tous incrémentés d’un, et OwningThread devient l’ID de thread de l’appelant. EntryCount et ContentionCount ne sont jamais décrémentés. Par exemple :
0:000> !critsec 433e60
CritSec mymodule!cs+0 at 00433E60
LockCount 0
RecursionCount 1
OwningThread 4d0
EntryCount 0
ContentionCount 0
À ce stade, quatre choses différentes peuvent se produire.
Le thread propriétaire appelle à nouveau EnterCriticalSection . Cela incrémente LockCount et RecursionCount. EntryCount n’est pas incrémenté.
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount 1 RecursionCount 2 OwningThread 4d0 EntryCount 0 ContentionCount 0
Un autre thread appelle EnterCriticalSection. Cela incrémente LockCount et EntryCount. RecursionCount n’est pas incrémenté.
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount 1 RecursionCount 1 OwningThread 4d0 EntryCount 1 ContentionCount 1
Le thread propriétaire appelle LeaveCriticalSection. Cela décrémente LockCount (à -1) et RecursionCount (à 0), et réinitialise OwningThread à 0.
0:000> !critsec 433e60 CritSec mymodule!cs+0 at 00433E60 LockCount NOT LOCKED RecursionCount 0 OwningThread 0 EntryCount 0 ContentionCount 0
Un autre thread appelle LeaveCriticalSection. Cela produit les mêmes résultats que le thread propriétaire appelant LeaveCriticalSection : il décrémente LockCount (à -1) et RecursionCount (à 0), et réinitialise OwningThread à 0.
Lorsqu’un thread appelle LeaveCriticalSection, Windows décrémente LockCount et RecursionCount. Cette fonctionnalité présente des aspects bons et mauvais. Il permet à un pilote de périphérique d’entrer une section critique sur un thread et de laisser la section critique sur un autre thread. Toutefois, cela permet également d’appeler accidentellement LeaveCriticalSection sur le mauvais thread, ou d’appeler LeaveCriticalSection trop de fois et d’amener LockCount à atteindre des valeurs inférieures à -1. Cela endommage la section critique et entraîne une attente indéfinie de tous les threads sur la section critique.
Interprétation de champs de section critiques dans Windows Server 2003 SP1 et versions ultérieures
Dans Microsoft Windows Server 2003 Service Pack 1 et versions ultérieures de Windows, le champ LockCount est analysé comme suit :
Le bit le plus bas indique le status de verrouillage. Si ce bit est 0, la section critique est verrouillée ; si elle est 1, la section critique n’est pas verrouillée.
Le bit suivant indique si un thread a été réveillé pour ce verrou. Si ce bit est 0, un thread a été réveillé pour ce verrou ; s’il est 1, aucun thread n’a été réveillé.
Les bits restants sont le complément unique du nombre de threads en attente du verrou.
Par exemple, supposons que LockCount a la valeur -22. Le bit le plus bas peut être déterminé de cette façon :
0:009> ? 0x1 & (-0n22)
Evaluate expression: 0 = 00000000
Le bit le plus bas suivant peut être déterminé de cette façon :
0:009> ? (0x2 & (-0n22)) >> 1
Evaluate expression: 1 = 00000001
Les éléments complémentaires des bits restants peuvent être déterminés de cette façon :
0:009> ? ((-1) - (-0n22)) >> 2
Evaluate expression: 5 = 00000005
Dans cet exemple, le premier bit est 0 et, par conséquent, la section critique est verrouillée. Le deuxième bit étant 1, aucun thread n’a été réveillé pour ce verrou. Le complément des bits restants est de 5, et il y a donc cinq threads en attente de ce verrou.
Informations supplémentaires
Pour plus d’informations sur la façon de déboguer des délais d’expiration de section critiques, consultez Délai d’expiration de section critique. Pour obtenir des informations générales sur les sections critiques, consultez le Microsoft Windows SDK, le Kit de pilotes Windows (WDK) ou Microsoft Windows Internals de Mark Russinovich et David Solomon.