首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 綜合教學

Linux 死鎖排查實戰:用 pstack 與 GDB 精準定位交叉持鎖線程

     編輯:Java 綜合教學

死鎖基礎知識回顧

死鎖發生需滿足四個必要條件(缺一不可):

  1. 互斥條件:資源同一時間只能被一個線程佔用
  2. 請求與保持:線程持有資源的同時請求新資源
  3. 不可剝奪:已獲得資源未釋放前不能強行搶佔
  4. 循環等待:多線程形成頭尾相接的等待環

典型交叉持鎖:Thread-1 鎖 A 等 B,Thread-2 鎖 B 等 A,雙方永久阻塞。

實驗環境與測試程式

編譯含死鎖邏輯的 C++ 程式(-g 保留除錯符號):

g++ -g lock.cpp -o lock -lpthread

程式建立 4 條線程,其中 Thread-1/Thread-2 分別以相反順序獲取 mutex1/mutex2,必然觸發死鎖。

工具介紹

工具用途
pstack輸出進程所有線程的呼叫堆疊,快速定位阻塞點
GDB深度除錯:檢查互斥鎖擁有者、切換線程上下文、查看原始碼行號

排查步驟實戰

1. 找到進程 PID

ps -ef | grep lock
# dyu  6721  5751  0 15:21 pts/3  00:00:00 ./lock

2. 連續多次執行 pstack 觀察堆疊變化

pstack 6721

關鍵觀察點:連續兩次輸出中,哪幾條線程堆疊完全不變且停留在 pthread_mutex_lock?

實驗中 Thread-4 (LWP 6722) 與 Thread-5 (LWP 6723) 兩次堆疊皆卡在 func1()/func2() 的 pthread_mutex_lock,判定為死鎖線程。

3. GDB 附著進程深度分析

gdb attach 6721
(gdb) info threads
# 列出所有線程及當前函數

切換至疑似死鎖線程,查看呼叫堆疊:

(gdb) thread 5
(gdb) where
#3  0x0000000000400a9b in func1 () at lock.cpp:18
#4  0x0000000000400ad7 in thread1 (arg=0x0) at lock.cpp:43

檢查互斥鎖內部結構,確認擁有者:

(gdb) p mutex1
$1 = {__data = {__lock = 2, __owner = 6722, ...}}  # 被 LWP 6722 擁有
(gdb) p mutex2
$3 = {__data = {__lock = 2, __owner = 6723, ...}}  # 被 LWP 6723 擁有}

鐵證如山:Thread-4 (LWP 6723) 想鎖 mutex1(被 6722 擁有),Thread-5 (LWP 6722) 想鎖 mutex2(被 6723 擁有),典型交叉持鎖。

原始碼層面修正

檢視 lock.cpp:18 與 :31 發現:

  • func1() 先鎖 mutex1 再鎖 mutex2
  • func2() 先鎖 mutex2 再鎖 mutex1

修正原則:全域統一鎖獲取順序(如按記憶體位址或自定義 ID 排序),打破循環等待條件。

預防死鎖的工程化建議

  • 避免長時間持有鎖,縮小臨界區
  • 使用 tryLock(timeout) 替代無限等待
  • 採用有序資源分配法或銀行家演算法
  • 程式碼審查時重點檢查多鎖獲取順序一致性
  • 啟用 ThreadSanitizer (TSan) 等動態檢測工具

總結

pstack 快速定範圍,GDB 精準定位鎖擁有者與等待鏈,兩者結合可在幾分鐘內鎖定死鎖根因。掌握四大必要條件與排查套路,讓死鎖無處遁形。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved