windows臨界區

臨界區:數組

臨界區是一種輕量級機制,在某一時間內只容許一個線程執行某個給定代碼段。一般在多線程修改全局數據時會使用臨界區。事件、信號量也用於多線程同步,但臨界區與它們不一樣,並不老是執行向內核模式的切換,這一轉換成本昂貴。要得到一個未佔用臨界區,事實上只須要對內存作出不多的修改,其速度很是快。只有在嘗試得到已佔用臨界區時,它纔會跳至內核模式。這一輕量級特性的缺點在於臨界區只能用於對同一進程內的線程進行同步。數據結構

臨界區由 WINNT.H 中所定義的 RTL_CRITICAL_SECTION 結構表示。 WINBASE.H 後您會發現:多線程

typedef RTL_CRITICAL_SECTION CRITICAL_SECTION;函數

操做臨界區的API函數有:工具

(1)初始化臨界區InitializeCriticalSection性能

(2)進入臨界區EnterCriticalSectionspa

(3)離開臨界區LeaveCriticalSection操作系統

(4)刪除臨界區DeleteCriticalSection線程

在臨界區未被使用的理想狀況中,對 EnterCriticalSection 的調用很是快速,由於它只是讀取和修改用戶模式內存中的內存位置。所阻止的線程之內核模式等待,在該臨界區的全部者將其釋放以前,不能對這些線程進行調度。若是有多個線程被阻止於一個臨界區中,當另外一線程釋放該臨界區時,只有一個線程得到該臨界區。指針

RTL_CRITICAL_SECTION 結構

一個進程的臨界區是保存於一個鏈表中,而且能夠對其進行枚舉。實際上,WINDBG 支持 !locks 命令,這一命令能夠列出目標進程中的全部臨界區。

RTL_CRITICAL_SECTION 結構以下:

struct RTL_CRITICAL_SECTION

{

    PRTL_CRITICAL_SECTION_DEBUG DebugInfo;

    LONG LockCount;

    LONG RecursionCount;

    HANDLE OwningThread;

    HANDLE LockSemaphore;

    ULONG_PTR SpinCount;

};

如下各段對每一個字段進行說明。

DebugInfo 此字段包含一個指針,指向系統分配的伴隨結構,該結構的類型爲 RTL_CRITICAL_SECTION_DEBUG。這一結構中包含更多極有價值的信息,也定義於 WINNT.H 中。LockCount 這是臨界區中最重要的一個字段。它被初始化爲數值 -1;此數值等於或大於 0 時,表示此臨界區被佔用。當其不等於 -1 時,OwningThread 字段包含了擁有此臨界區的線程 ID。此字段與 (RecursionCount-1) 數值之間的差值表示有多少個其餘線程在等待得到該臨界區。

RecursionCount 此字段包含全部者線程已經得到該臨界區的次數。若是該數值爲零,下一個嘗試獲取該臨界區的線程將會成功。

OwningThread 此字段包含當前佔用此臨界區的線程的線程標識符。此線程 ID 與 GetCurrentThreadId 之類的 API 所返回的 ID 相同。

LockSemaphore 它是一個內核對象句柄,用於通知操做系統:該臨界區如今空閒。操做系統在一個線程第一次嘗試得到該臨界區,但被另外一個已經擁有該臨界區的線程所阻止時,自動建立這樣一個句柄。應當調用 DeleteCriticalSection(它將發出一個調用該事件的 CloseHandle 調用,並在必要時釋放該調試結構),不然將會發生資源泄漏。

SpinCount 僅用於多處理器系統。MSDN文檔對此字段進行以下說明:「在多處理器系統中,若是該臨界區不可用,調用線程將在對與該臨界區相關的信號執行等待操做以前,旋轉 dwSpinCount 次。若是該臨界區在旋轉操做期間變爲可用,該調用線程就避免了等待操做。」旋轉計數能夠在多處理器計算機上提供更佳性能,其緣由在於在一個循環中旋轉一般要快於進入內核模式等待狀態。此字段默認值爲零,但能夠用 InitializeCriticalSectionAndSpinCount API 將其設置爲一個不一樣值。

RTL_CRITICAL_SECTION_DEBUG結構以下:

struct _RTL_CRITICAL_SECTION_DEBUG

{

    WORD   Type;

    WORD   CreatorBackTraceIndex;

    RTL_CRITICAL_SECTION *CriticalSection;

    LIST_ENTRY ProcessLocksList;

    DWORD EntryCount;

    DWORD ContentionCount;

    DWORD Spare[ 2 ];

}

這一結構由InitializeCriticalSection分配和初始化。它既能夠由NTDLL內的預分配數組分配,也能夠由進程堆分配。RTL_CRITICAL_SECTION的這一伴隨結構包含一組匹配字段,具備迥然不一樣的角色:有兩個難以理解,隨後兩個提供了理解這一臨界區鏈結構的關鍵,兩個是重複設置的,最後兩個未使用。

下面是對 RTL_CRITICAL_SECTION 字段的說明。

Type 此字段未使用,被初始化爲數值 0。

CreatorBackTraceIndex 此字段僅用於診斷情形中。在註冊表項 HKLM\Software\Microsoft\Windows NT\CurrentVersion\Image File Execution Options\YourProgram 之下是 keyfield、GlobalFlag 和 StackTraceDatabaseSizeInMb 值。注意,只有在運行稍後說明的 Gflags 命令時纔會顯示這些值。這些註冊表值的設置正確時,CreatorBackTraceIndex 字段將由堆棧跟蹤中所用的一個索引值填充。在 MSDN 中搜索 GFlags 文檔中的短語「create user mode stack trace database」和「enlarging the user-mode stack trace database」,能夠找到有關這一內容的更多信息。

CriticalSection 指向與此結構相關的 RTL_CRITICAL_SECTION。圖 1 說明該基礎結構以及 RTL_CRITICAL_SECTION、RTL_CRITICAL_SECTION_DEBUG 和事件鏈中其餘參與者之間的關係。

 

圖 1 臨界區處理流程

ProcessLocksList LIST_ENTRY 是用於表示雙向鏈表中節點的標準 Windows 數據結構。RTL_CRITICAL_SECTION_DEBUG 包含了鏈表的一部分,容許向前和向後遍歷該臨界區。本文後面給出的實用工具說明如何使用 Flink(前向連接)和 Blink(後向連接)字段在鏈表中的成員之間移動。任何從事過設備驅動程序或者研究過 Windows 內核的人都會很是熟悉這一數據結構。

EntryCount/ContentionCount 這些字段在相同的時間、出於相同的緣由被遞增。這是那些由於不能立刻得到臨界區而進入等待狀態的線程的數目。與 LockCount 和 RecursionCount 字段不一樣,這些字段永遠都不會遞減。

Spares 這兩個字段未使用,甚至未被初始化(儘管在刪除臨界區結構時將這些字段進行了清零)。後面將會說明,能夠用這些未被使用的字段來保存有用的診斷值。

總結:

(1)若是 LockCount 字段有一個不等於 -1 的數值,此臨界區被佔用,OwningThread 字段包含擁有該臨界區的線程的線程標識符。

(2)若是 RecursionCount 是一個大於 1 的數值,其告知您全部者線程已經從新得到該臨界區多少次(也許沒必要要)。

(3)LockCount 與 RecursionCount 字段中分別包含其初始值 -1 和 0,這一點很是重要。事實上,對於單線程程序,不能僅經過檢查這些字段來判斷是否曾得到過臨界區。可是,多線程程序留下了一些標記,能夠用來判斷是否有兩個或多個線程試圖同時擁有同一臨界區。

(4)在該臨界區未被佔用時 LockSemaphore 字段中仍包含一個非零值。這表示:在某一時間,此臨界區阻止了一個或多個線程。事件句柄用於通知該臨界區已被釋放,等待該臨界區的線程之一如今能夠得到該臨界區並繼續執行。由於 OS 在臨界區阻止另外一個線程時自動分配事件句柄,因此若是您在再也不須要臨界區時忘記將其刪除,LockSemaphore 字段可能會致使程序中發生資源泄漏。

(5)在多線程程序中可能遇到的另外一狀態是 EntryCount 和 ContentionCount 字段包含一個大於零的數值。這兩個字段保存有臨界區對一個線程進行阻止的次數。在每次發生這一事件時,這兩個字段被遞增,但在臨界區存在期間不會被遞減。這些字段可用於間接肯定程序的執行路徑和特性。例如,EntryCount 很是高時則意味着該臨界區經歷着大量爭用,可能會成爲代碼執行過程當中的一個潛在瓶頸。

(6)能夠經過RTL_CRITICAL_SECTION_DEBUG 中的LIST_ENTRY 遍歷進程中的臨界區,Flink=NULL爲表頭,Blink=NULL爲表尾。

(7)利用RTL_CRITICAL_SECTION 的 Spare 字段能夠區分咱們定義的臨界區和系統定義的臨界區。

可是如何知道哪些線程等待某個臨界區呢?

相關文章
相關標籤/搜索