(Windbg調試四)C++死鎖問題定位與分析


 

     C++程序異常一般有兩種表現形式:程序崩潰和程序無響應。程序崩潰主要是由指針,數組越界等原因引起,這種情況可以直接通過在程序中加入Dump捕捉邏輯,分析dump文件,定位出崩潰的代碼。程序無響應主要是由死循環和死鎖兩個原因造成的,死循環我們可以通過查看CPU使用情況來初步判斷,然后轉存為Dump進行分析;死鎖問題是C++中最難定位和分析的一種程序異常問題,下面我們主要介紹下死鎖問題的一般定位和分析方法。

一,測試代碼

     在介紹死鎖分析方法之前,先看一下本文中我們使用的測試代碼:

CRITICAL_SECTION cs1;
CRITICAL_SECTION cs2;

DWORD WINAPI Thread1(LPVOID lpParameter);
DWORD WINAPI Thread2(LPVOID lpParameter);

int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
{
	//初始化關鍵代碼段
	InitializeCriticalSection(&cs1);
	InitializeCriticalSection(&cs2);

	//創建線程
	HANDLE hThread1 = CreateThread(NULL, 0, Thread1, NULL, 0, NULL);
	HANDLE hThread2 = CreateThread(NULL, 0, Thread2, NULL, 0, NULL);

	//等待線程結束
	WaitForSingleObject(hThread1, INFINITE);
	WaitForSingleObject(hThread2, INFINITE);
	Sleep(2000);

	//關閉線程句柄
	CloseHandle(hThread1);
	CloseHandle(hThread2);

	//釋放關鍵代碼段
	DeleteCriticalSection(&cs1);
	DeleteCriticalSection(&cs2);

	return 0;
}

DWORD WINAPI Thread1(LPVOID lpParameter)
{
	for (int i = 0; i < 10; i++)
	{
		EnterCriticalSection(&cs1);
		Sleep(500);
		EnterCriticalSection(&cs2);

		std::cout << "Thread1" << std::endl;

		LeaveCriticalSection(&cs2);
		LeaveCriticalSection(&cs1);
	}
	return 1;
}

DWORD WINAPI Thread2(LPVOID lpParameter)
{
	for (int i = 0; i < 10; i++)
	{
		EnterCriticalSection(&cs2);
		Sleep(500);
		EnterCriticalSection(&cs1);

		std::cout << "Thread2" << std::endl;

		LeaveCriticalSection(&cs1);
		LeaveCriticalSection(&cs2);
	}
	return 1;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63

在這個例子中,Thread1等待Thread2,Thread2等待Thread1,同時主線程等待着Thread1和Thread2,三個線程陷入死鎖狀態。

二,等待鏈

在這里插入圖片描述
     等待鏈是線程和同步對象的交替序列;每個線程都在等待它后面的對象,該對象由鏈中的后續線程擁有。通過分析等待鏈,我們可以清晰地看到各個子線程/子進程掛起所在等待的下一個線程/進程。(等待鏈更多知識請轉至Wait Chain Traversal
     在《Windows核心編程》一書附帶的源碼09-LockCop中,詳細介紹了WCT的編碼實現,感興趣的可以去閱讀一下源碼,附上下載鏈接(Windows核心編程(第5版中文版) 源碼)。編譯09-LockCop,我們可以得到09-LockCop.exe應用程序,打開改程序,選擇我們的進程DeadLockTest.exe,分析如下:
在這里插入圖片描述
     線程8248等待線程9524,9524在等待6432,而6432又在等待9524,陷入了互相等待的處境中,導致死鎖。因此可以確認DeadLockTest.exe的無響應是由死鎖造成的。
     我們也可以直接通過windows的任務管理器來檢測一個程序是否陷入死鎖,打開任務管理器-性能-資源監視器,選中我們的測試程序,右鍵菜單“分析等待鏈”:
在這里插入圖片描述
     分析結果如下:
在這里插入圖片描述
     可以看到9524線程與6432線程陷入了互相等待鎖資源的處境,程序死鎖。

三,死鎖的定位和分析

     如果死鎖的程序剛好在我們自己的開發機器上,那么使用WinDbg的Attach To A Proccess功能將死鎖程序直接附加到WinDbg中進行分析;如果不在我們的機器上,可以通過Windows的資源管理器對進行進行創建轉儲文件轉換為dump(注意32位和64位的區別),再通過WinDbg對dump進行分析。為了方便,我們直接以Attach方式講解。
     Attach程序后,輸入!locks命令查看鎖的狀態:
在這里插入圖片描述
     總共有兩個鎖cs1(012e4394)和cs2(012e437c),這兩個鎖的LockCount(表示還有多少個線程在等待這個臨界區)都為1,說明這兩個鎖都處於等待狀態。
     再輸入~*kb查看一下當前各個線程的堆棧:
在這里插入圖片描述
     線程2534和線程1920都處於等待狀態,線程2534等待鎖012e437c,線程1920等待鎖012e4394。再結合上面的鎖信息可以知道,鎖012e4394被線程2534擁有,鎖012e437c被線程1920擁有,所以這兩個線程陷入了互相等待的死鎖中。等待鎖的源碼也直接定位到了,問題解決。

本文舉例比較簡單,主要是介紹死鎖問題的定位以及分析的一般方法,具體問題還要視情況具體分析。

from:https://blog.csdn.net/bajianxiaofendui/article/details/86983550


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM