《Windows via C/C++》学习笔记 —— “线程同步”之“检测死锁”
本來這篇內容在書中是在“其他線程同步函數”這一節中的。這節中介紹了另外的幾個等待函數,比如WaitForInputIdle、MsgWaitForMultipleObjects、WaitForDebugEvent,感覺用途不大,只有SignalObjectAndWait這個函數用途比較大,該函數筆者已經在前面的“等待函數”這一篇中寫過了。
本篇主要討論這本書新增加的內容,就是Windows Vista中提供的調查死鎖的方法。
?
在Windows Vista中,提供了用來“檢測線程死鎖”的一組API,Jeffrey Richter先生在書中將其稱為“Wait Chain Traversal”(WCT)API。查看了網上的翻譯,叫做“等待鏈遍歷”API函數,我也不知道這么翻譯是否正確或權威,就姑且這么叫著吧,下面簡稱為WCT。
WCT函數允許你列出“鎖”,并檢測死鎖,無論是單個進程的,還是跨進程的。Windows對以下線程同步機制的鎖保持著跟蹤:
| 關鍵代碼段 | Windows對關鍵代碼段屬于哪個線程保持著跟蹤 |
| 互斥內核對象 | Windows對互斥內核對象屬于哪個線程保持著跟蹤,即使互斥內核對象被丟棄 |
| 進程和線程 | Windows對一個線程正在等待哪個進程或線程保持著跟蹤,另外跟蹤進程和線程是否終止 |
| SendMessage | Windows保持跟蹤正在等待SendMessage函數返回的線程 |
| COM初始化和調用 | 調用CoCreateInstance函數或COM對象的方法的線程被跟蹤 |
| 高級本地過程調用(ALPC) | 在Windows Vista中,ALPC代替了LPC,新的沒有文檔的內核進程內部的通信(IPC)機制 |
?
需要注意的是,讀寫鎖(SRWLock)同步機制沒有被WPC所跟蹤,另外某些內核對象,比如事件內核對象、信號量、等待定時器沒有被跟蹤。
?
本書舉了一個例子來演示了WPC函數的使用。本人總結了下,主要通過以下幾個步驟來實現死鎖的檢測:
1、打開等待鏈:使用OpenThreadWaitChainSession函數
2、如果需要檢測COM的話:使用RegisterWaitChainCOMCallback函數
3、取得等待鏈信息:使用GetThreadWaitChain函數
4、關閉等待鏈:使用CloseThreadWaitChainSession函數
?
首先,介紹一下OpenThreadWaitChainSession函數:
HWCT?OpenThreadWaitChainSession(???DWORD?dwFlags,???????//0表示同步,WCT_ASYNC_OPEN_FLAG表示異步
???PWAITCHAINCALLBACK?callback);???//異步的話要設置該回調函數指針
?
該函數成功,返回一個HWCT類型的句柄,該句柄要傳遞給CloseThreadWaitChainSession函數,在最后釋放。如果失敗,該函數返回NULL。
一般地,您總是希望從中調用RegisterWaitChainCOMCallback函數以注冊 CoGetCallState和CoGetActivationState這兩個COM函數,以便WCT能夠報告 COM所有權信息,由于并未記錄這些函數,您需要從ole32.dll通過GetProcAddress 獲得這些函數。本人認為,在您對WCT API進行初始化以查找COM死鎖時,WCT API 應自動處理這種細小問題。
?
VOID?WINAPI?RegisterWaitChainCOMCallback(???__in??PCOGETCALLSTATE?CallStateCallback,???//CoGetCallState函數地址
???__in??PCOGETACTIVATIONSTATE?
?????????????ActivationStateCallback????//CoGetActivationState函數地址
);
?
你可以使用如下代碼這樣使用RegisterWaitChainCOMCallback函數:
?
PCOGETCALLSTATE?CallStateCallback;??????//函數指針PCOGETACTIVATIONSTATE?ActivationStateCallback;?????//函數指針
HMODULE?hOLE32DLL?=?LoadLibrary(TEXT("OLE32.DLL"));//加載OLE32.DLL
//從ole32.dll中取得函數地址
CallStateCallback?=?(PCOGETCALLSTATE)
?????????????????????GetProcAddress(_hOLE32DLL,?"CoGetCallState");
ActivationStateCallback?=?(PCOGETACTIVATIONSTATE)
?????????????GetProcAddress(_hOLE32DLL,?"CoGetActivationState");?
//在等待鏈中注冊COM函數
RegisterWaitChainCOMCallback(CallStateCallback,ActivationStateCallback);
?
下面,第3步,使用GetThreadWaitChain函數取得等待鏈信息:
BOOL?WINAPI?GetThreadWaitChain(???HWCT?hWctSession,??????????//等待鏈句柄
???DWORD_PTR?pContext,?????//異步方法中回調函數的參數
???DWORD?dwFlags,??????????????//查詢類型(位或組合)
???DWORD?TID,????????????????????//要查詢的線程的線程ID
???PDWORD?pNodeCount,??????//返回值,指明等待鏈中的結點個數
???PWAITCHAIN_NODE_INFO?pNodeInfoArray,?????//返回值,等待鏈結點信息
???LPBOOL?pbIsCycle?????????????//返回值,TURE表示死鎖
);
?
pNodeCount參數如果返回的DWORD數據值為0,表明一個問題產生了,比如拒絕訪問。這個時候可以調用GetLastError取得錯誤信息。
這個函數中,pNodeInfoArray函數是一個WAITCHAIN_NODE_INFO結構的指針,該結構如下:
?
typedef?struct?_WAITCHAIN_NODE_INFO{
????WCT_OBJECT_TYPE?ObjectType;??????????//結點對象類型
????WCT_OBJECT_STATUS?ObjectStatus;???//結點對象狀態
????union?{
????????struct?{
????????????WCHAR?ObjectName[WCT_OBJNAME_LENGTH];
????????????LARGE_INTEGER?Timeout;????//?Not?implemented?in?v1
????????????BOOL?Alertable;???????????//?Not?implemented?in?v1
????????}?LockObject;
???????struct?{
???????????DWORD?ProcessId;
???????????DWORD?ThreadId;
???????????DWORD?WaitTime;
???????????DWORD?ContextSwitches;
???????}?ThreadObject;
????};
}?WAITCHAIN_NODE_INFO,?*PWAITCHAIN_NODE_INFO;
?
該結構中的ObjectTyped成員,指明了當前等待鏈中的結點類型,ObjectStatus指明了當前該結點對象的狀態,兩者具體取值就看MSDN或本書吧。如果這個結點類型是一個“阻塞”狀態的線程,即ObjectTyped為WctThreadType的話,內部的ThreadObject成員即有效。
?
最后,不要忘記調用CloseThreadWaitChainSession關閉WCT句柄。當然,如果用LoadLibrary函數加載了OLE23.dll文件的話,也要記得FreeLibrary函數來釋放。
?
可以使用如下代碼處理等待鏈中的結點:
?
HWCT?hWCTSession?=?OpenThreadWaitChainSession(0,?NULL);if?(hWCTSession?==?NULL)
?????return?0;
//兩個COM的函數指針
PCOGETCALLSTATE?CallStateCallback;
PCOGETACTIVATIONSTATE?ActivationStateCallback;
HMODULE?hOLE32DLL?=?LoadLibrary(TEXT("OLE32.DLL"));???//加載OLE32.DLL
if?(hOLE32DLL?==?NULL)
?????return?0;
//從ole32.dll中取得兩個函數的地址
CallStateCallback?=?(PCOGETCALLSTATE)
?????????????????????GetProcAddress(_hOLE32DLL,?"CoGetCallState");
ActivationStateCallback?=?(PCOGETACTIVATIONSTATE)
?????????????????????GetProcAddress(_hOLE32DLL,?"CoGetActivationState");
//注冊COM函數,使得WCT可以報告COM相關事件
RegisterWaitChainCOMCallback(CallStateCallback,ActivationStateCallback);
//在你需要處理的線程中:
DWORD?dwNodesInChain?=?0;?????//鏈中的結點個數
BOOL?bDeadlock?=?FALSE;???????//線程是否存在死鎖
WAITCHAIN_NODE_INFO?chain[WCT_MAX_NODE_COUNT];//WCT節點結構數組
//取得等待鏈信息,并判斷是否正確,TID為線程ID
if?(!GetThreadWaitChain(hWCTSession,?NULL,?WCTP_GETINFO_ALL_FLAGS,
?????????TID,?&dwNodesInChain,?chain,?&bDeadlock))
{
?????//函數調用失敗,在此處理
??????//
?????return?0;
}
//?開始處理等待鏈
dwNodesInChain?=?min(dwNodesInChain,?WCT_MAX_NODE_COUNT);
if?(dwNodesInChain?==?0)?????//結點個數為0,可以調用GetLastError處理
?????return?0;
//可以在此處理本線程有關的等待鏈信息
if?(bDeadlock)
?????//表明線程ID為TID的線程存在死鎖
//處理每個鏈中的節點
for?(DWORD?current?=?0;?current?<?dwNodesInChain;?++current)
{
?????//在此處理練中每個結點信息
??????//
}
FreeLibrary(hOLE32DLL);????//釋放COM模塊
CloseThreadWaitChainSession(hWCTSession);????//關閉WCT
?
轉載于:https://www.cnblogs.com/wz19860913/archive/2008/08/15/1268901.html
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的《Windows via C/C++》学习笔记 —— “线程同步”之“检测死锁”的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 解决EXCEL统计问题的分享
- 下一篇: C++ enum 枚举