死鎖 (deallocks): 是指兩個或兩個以上的進程(線程)在執行過程當中,因爭奪資源而形成的一種互相等待的現象,若無外力做用,它們都將沒法推動下去。此時稱系統處於死鎖狀態或系統產生了死鎖,這些永遠在互相等待的進程(線程)稱爲死鎖進程(線程)。 因爲資源佔用是互斥的,當某個進程提出申請資源後,使得有關進程(線程)在無外力協助下,永遠分配不到必需的資源而沒法繼續運行,這就產生了一種特殊現象死鎖。linux
一種交叉持鎖死鎖的情形,此時執行程序中兩個或多個線程發生永久堵塞(等待),每一個線程都在等待被其它線程佔用並堵塞了的資源。例如,若是線程 1 鎖住了記錄 A 並等待記錄 B,而線程 2 鎖住了記錄 B 並等待記錄 A,這樣兩個線程就發生了死鎖現象。在計算機系統中 , 若是系統的資源分配策略不當,更常見的多是程序員寫的程序有錯誤等,則會致使進程因競爭資源不當而產生死鎖的現象。程序員
(1) 互斥條件:一個資源每次只能被一個進程(線程)使用。
(2) 請求與保持條件:一個進程(線程)因請求資源而阻塞時,對已得到的資源保持不放。
(3) 不剝奪條件 : 此進程(線程)已得到的資源,在末使用完以前,不能強行剝奪。
(4) 循環等待條件 : 多個進程(線程)之間造成一種頭尾相接的循環等待資源關係。算法
註釋:在執行 func2 和 func4 以後,子線程 1 得到了鎖 A,正試圖得到鎖 B,可是子線程 2 此時得到了鎖 B,正試圖得到鎖 A,因此子線程 1 和子線程 2 將沒有辦法獲得鎖 A 和鎖 B,由於它們各自被對方佔有,永遠不會釋放,因此發生了死鎖的現象。函數
pstack 是 Linux(好比 Red Hat Linux 系統、Ubuntu Linux 系統等)下一個頗有用的工具,它的功能是打印輸出此進程的堆棧信息。能夠輸出全部線程的調用關係棧。工具
GDB 是 GNU 開源組織發佈的一個強大的 UNIX 下的程序調試工具。Linux 系統中包含了 GNU 調試程序 gdb,它是一個用來調試 C 和 C++ 程序的調試器。可使程序開發者在程序運行時觀察程序的內部結構和內存的使用狀況 .測試
gdb 所提供的一些主要功能以下所示:spa
1 運行程序,設置能影響程序運行的參數和環境 ;.net
2 控制程序在指定的條件下中止運行;線程
3 當程序中止時,能夠檢查程序的狀態;設計
4 當程序 crash 時,能夠檢查 core 文件;
5 能夠修改程序的錯誤,並從新運行程序;
6 能夠動態監視程序中變量的值;
7 能夠單步執行代碼,觀察程序的運行狀態。
gdb 程序調試的對象是可執行文件或者進程,而不是程序的源代碼文件。然而,並非全部的可執行文件均可以用 gdb 調試。若是要讓產生的可執行文件能夠用來調試,需在執行 g++(gcc)指令編譯程序時,加上 -g 參數,指定程序在編譯時包含調試信息。調試信息包含程序裏的每一個變量的類型和在可執行文件裏的地址映射以及源代碼的行號。gdb 利用這些信息使源代碼和機器碼相關聯。gdb 的基本命令較多,不作詳細介紹,你們若是須要進一步瞭解,請參見 gdb 手冊。
#include <unistd.h> #include <pthread.h> #include <string.h> pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex3 = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t mutex4 = PTHREAD_MUTEX_INITIALIZER; static int sequence1 = 0; static int sequence2 = 0; int func1() { pthread_mutex_lock(&mutex1); ++sequence1; sleep(1); pthread_mutex_lock(&mutex2); ++sequence2; pthread_mutex_unlock(&mutex2); pthread_mutex_unlock(&mutex1); return sequence1; } int func2() { pthread_mutex_lock(&mutex2); ++sequence2; sleep(1); pthread_mutex_lock(&mutex1); ++sequence1; pthread_mutex_unlock(&mutex1); pthread_mutex_unlock(&mutex2); return sequence2; } void* thread1(void* arg) { while (1) { int iRetValue = func1(); if (iRetValue == 100000) { pthread_exit(NULL); } } } void* thread2(void* arg) { while (1) { int iRetValue = func2(); if (iRetValue == 100000) { pthread_exit(NULL); } } } void* thread3(void* arg) { while (1) { sleep(1); char szBuf[128]; memset(szBuf, 0, sizeof(szBuf)); strcpy(szBuf, "thread3"); } } void* thread4(void* arg) { while (1) { sleep(1); char szBuf[128]; memset(szBuf, 0, sizeof(szBuf)); strcpy(szBuf, "thread3"); } } int main() { pthread_t tid[4]; if (pthread_create(&tid[0], NULL, &thread1, NULL) != 0) { _exit(1); } if (pthread_create(&tid[1], NULL, &thread2, NULL) != 0) { _exit(1); } if (pthread_create(&tid[2], NULL, &thread3, NULL) != 0) { _exit(1); } if (pthread_create(&tid[3], NULL, &thread4, NULL) != 0) { _exit(1); } sleep(5); //pthread_cancel(tid[0]); pthread_join(tid[0], NULL); pthread_join(tid[1], NULL); pthread_join(tid[2], NULL); pthread_join(tid[3], NULL); pthread_mutex_destroy(&mutex1); pthread_mutex_destroy(&mutex2); pthread_mutex_destroy(&mutex3); pthread_mutex_destroy(&mutex4); return 0; }
[dyu@xilinuxbldsrv purify]$ g++ -g lock.cpp -o lock -lpthread
[dyu@xilinuxbldsrv purify]$ ps -ef|grep lock dyu 6721 5751 0 15:21 pts/3 00:00:00 ./lock
[dyu@xilinuxbldsrv purify]$ pstack 6721 Thread 5 (Thread 0x41e37940 (LWP 6722)): #0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 #1 0x0000003d1a808e1a in _L_lock_1034 () from /lib64/libpthread.so.0 #2 0x0000003d1a808cdc in pthread_mutex_lock () from /lib64/libpthread.so.0 #3 0x0000000000400a9b in func1() () #4 0x0000000000400ad7 in thread1(void*) () #5 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #6 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 4 (Thread 0x42838940 (LWP 6723)): #0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 #1 0x0000003d1a808e1a in _L_lock_1034 () from /lib64/libpthread.so.0 #2 0x0000003d1a808cdc in pthread_mutex_lock () from /lib64/libpthread.so.0 #3 0x0000000000400a17 in func2() () #4 0x0000000000400a53 in thread2(void*) () #5 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #6 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 3 (Thread 0x43239940 (LWP 6724)): #0 0x0000003d19c9a541 in nanosleep () from /lib64/libc.so.6 #1 0x0000003d19c9a364 in sleep () from /lib64/libc.so.6 #2 0x00000000004009bc in thread3(void*) () #3 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #4 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 2 (Thread 0x43c3a940 (LWP 6725)): #0 0x0000003d19c9a541 in nanosleep () from /lib64/libc.so.6 #1 0x0000003d19c9a364 in sleep () from /lib64/libc.so.6 #2 0x0000000000400976 in thread4(void*) () #3 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #4 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 1 (Thread 0x2b984ecabd90 (LWP 6721)): #0 0x0000003d1a807b35 in pthread_join () from /lib64/libpthread.so.0 #1 0x0000000000400900 in main ()
[dyu@xilinuxbldsrv purify]$ pstack 6721 Thread 5 (Thread 0x40bd6940 (LWP 6722)): #0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 #1 0x0000003d1a808e1a in _L_lock_1034 () from /lib64/libpthread.so.0 #2 0x0000003d1a808cdc in pthread_mutex_lock () from /lib64/libpthread.so.0 #3 0x0000000000400a87 in func1() () #4 0x0000000000400ac3 in thread1(void*) () #5 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #6 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 4 (Thread 0x415d7940 (LWP 6723)): #0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 #1 0x0000003d1a808e1a in _L_lock_1034 () from /lib64/libpthread.so.0 #2 0x0000003d1a808cdc in pthread_mutex_lock () from /lib64/libpthread.so.0 #3 0x0000000000400a03 in func2() () #4 0x0000000000400a3f in thread2(void*) () #5 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #6 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 3 (Thread 0x41fd8940 (LWP 6724)): #0 0x0000003d19c7aec2 in memset () from /lib64/libc.so.6 #1 0x00000000004009be in thread3(void*) () #2 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #3 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 2 (Thread 0x429d9940 (LWP 6725)): #0 0x0000003d19c7ae0d in memset () from /lib64/libc.so.6 #1 0x0000000000400982 in thread4(void*) () #2 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #3 0x0000003d19cd40cd in clone () from /lib64/libc.so.6 Thread 1 (Thread 0x2af906fd9d90 (LWP 6721)): #0 0x0000003d1a807b35 in pthread_join () from /lib64/libpthread.so.0 #1 0x0000000000400900 in main ()
連續屢次查看這個進程的函數調用關係堆棧進行分析:當進程吊死時,屢次使用 pstack 查看進程的函數調用堆棧,死鎖線程將一直處於等鎖的狀態,對比屢次的函數調用堆棧輸出結果,肯定哪兩個線程(或者幾個線程)一直沒有變化且一直處於等鎖的狀態(可能存在兩個線程 一直沒有變化)。
輸出分析:
根據上面的輸出對比能夠發現,線程 1 和線程 2 由第一次 pstack 輸出的處在 sleep 函數變化爲第二次 pstack 輸出的處在 memset 函數。可是線程 4 和線程 5 一直處在等鎖狀態(pthread_mutex_lock),在連續兩次的 pstack 信息輸出中沒有變化,因此咱們能夠推測線程 4 和線程 5 發生了死鎖。
Gdb into thread輸出:
(gdb) info thread 5 Thread 0x41e37940 (LWP 6722) 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 4 Thread 0x42838940 (LWP 6723) 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 3 Thread 0x43239940 (LWP 6724) 0x0000003d19c9a541 in nanosleep () from /lib64/libc.so.6 2 Thread 0x43c3a940 (LWP 6725) 0x0000003d19c9a541 in nanosleep () from /lib64/libc.so.6 * 1 Thread 0x2b984ecabd90 (LWP 6721) 0x0000003d1a807b35 in pthread_join () from /lib64/libpthread.so.0
(gdb) thread 5 [Switching to thread 5 (Thread 0x41e37940 (LWP 6722))]#0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 (gdb) where #0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 #1 0x0000003d1a808e1a in _L_lock_1034 () from /lib64/libpthread.so.0 #2 0x0000003d1a808cdc in pthread_mutex_lock () from /lib64/libpthread.so.0 #3 0x0000000000400a9b in func1 () at lock.cpp:18 #4 0x0000000000400ad7 in thread1 (arg=0x0) at lock.cpp:43 #5 0x0000003d1a80673d in start_thread () from /lib64/libpthread.so.0 #6 0x0000003d19cd40cd in clone () from /lib64/libc.so.6
(gdb) f 3 #3 0x0000000000400a9b in func1 () at lock.cpp:18 18 pthread_mutex_lock(&mutex2); (gdb) thread 4 [Switching to thread 4 (Thread 0x42838940 (LWP 6723))]#0 0x0000003d1a80d4c4 in __lll_lock_wait () from /lib64/libpthread.so.0 (gdb) f 3 #3 0x0000000000400a17 in func2 () at lock.cpp:31 31 pthread_mutex_lock(&mutex1); (gdb) p mutex1 $1 = {__data = {__lock = 2, __count = 0, __owner = 6722, __nusers = 1, __kind = 0, __spins = 0, __list = {__prev = 0x0, __next = 0x0}}, __size = "\002\000\000\000\000\000\000\000B\032\000\000\001", '\000' <repeats 26 times>, __align = 2} (gdb) p mutex3 $2 = {__data = {__lock = 0, __count = 0, __owner = 0, __nusers = 0, __kind = 0, __spins = 0, __list = {__prev = 0x0, __next = 0x0}}, __size = '\000' <repeats 39 times>, __align = 0} (gdb) p mutex2 $3 = {__data = {__lock = 2, __count = 0, __owner = 6723, __nusers = 1, __kind = 0, __spins = 0, __list = {__prev = 0x0, __next = 0x0}}, __size = "\002\000\000\000\000\000\000\000C\032\000\000\001", '\000' <repeats 26 times>, __align = 2} (gdb)
從上面能夠發現,線程 4 正試圖得到鎖 mutex1,可是鎖 mutex1 已經被 LWP 爲 6722 的線程獲得(__owner = 6722),線程 5 正試圖得到鎖 mutex2,可是鎖 mutex2 已經被 LWP 爲 6723 的 獲得(__owner = 6723),從 pstack 的輸出能夠發現,LWP 6722 與線程 5 是對應的,LWP 6723 與線程 4 是對應的。因此咱們能夠得出, 線程 4 和線程 5 發生了交叉持鎖的死鎖現象。查看線程的源代碼發現,線程 4 和線程 5 同時使用 mutex1 和 mutex2,且申請順序不合理。
本文簡單介紹了一種在 Linux 平臺下分析死鎖問題的方法,對一些死鎖問題的分析有必定做用。但願對你們有幫助。理解了死鎖的緣由,尤爲是產生死鎖的四個必要條件,就能夠最大可能地避免、預防和解除死鎖。因此,在系統設計、進程調度等方面注意如何不讓這四個必要條件成立,如何肯定資源的合理分配算法,避免進程永久佔據系統資源。此外,也要防止進程在處於等待狀態的狀況下佔用資源 , 在系統運行過程當中,對進程發出的每個系統可以知足的資源申請進行動態檢查,並根據檢查結果決定是否分配資源,若分配後系統可能發生死鎖,則不予分配,不然予以分配。所以,對資源的分配要給予合理的規劃,使用有序資源分配法和銀行家算法等是避免死鎖的有效方法。