1. 程式人生 > >用gdb指令碼解決死鎖的除錯方法(由pthread_mutex_lock引起的死鎖)

用gdb指令碼解決死鎖的除錯方法(由pthread_mutex_lock引起的死鎖)

首先給出gdb定位死鎖的指令碼:

#deadlock_debug_gdb.cmd
set pagination off	
set logging file gdb.log
set logging overwrite
set logging on

start						
set $lock_addr=pthread_mutex_lock
set $unlock_addr=pthread_mutex_unlock
break *$lock_addr
break *$unlock_addr

while 1
	continue
	if $pc != $lock_addr && $pc != $unlock_addr
		quit
	end
	bt
end 

#不出現Type<Enter>to continue的提示訊息
#設定日誌檔案gdb.log
#寫日誌模式為覆蓋寫
#開啟日誌功能

#在main函式第一句設定斷點並開始執行程式
#記錄lock和unlock的系統函式地址並給他們設定斷點
#除錯物件程式執行後若出現死鎖,立刻結束程式(quit,等效使用者Ctrl-C)
#bt就是每次有鎖操作時就列印堆疊到log

該gdb指令碼deadlock_debug_gdb.cmd的功能主要是:

此命令檔案會在pthread_mutex_lock()和pthread_mutex_unlock()被呼叫時顯示backtrace,並將backtrace資訊記錄到gdb.log日誌檔案中。

使用gdb指令碼deadlock_debug_gdb.cmd的除錯死鎖方法是:

step1:gdb除錯執行a.out程式,執行時自行執行gdb指令碼檔案

$gdb    ./a.out      -x     ./deadlock_debug_gdb.cmd

.........

當a.out死鎖時,程式會被gdb指令碼中斷,從a.out開始執行到出現死鎖中斷的整個過程中pthread_mutex_lock和pthread_mutex_unlock的堆疊都在日誌中gdb.log

step2:使用shell命令找到gdb.log中所有包含pthread_mutex_lock和pthread_mutex_unlock的所有行

$cat gdb.log | grep -Al "^#0.*pthread_mutex_" | set s/from\.*$// | sed s/.*\in\//

............

pthread_mutex_lock()

thr (arg=0x0) at a.out.c : 18

............

pthread_mutex_lock()

cnt_reset() at a.out.c:10

又上可知,最後a.out.c原始檔在18號加鎖後(pthread_mutex_lock()),又在第10行再次加鎖(pthread_mutex_lock())導致了a.out的死鎖。

下面要做的就是分析下為啥18行加鎖後沒有釋放鎖導致了第10行加鎖時導致了死鎖。

以上就是使用gdb指令碼除錯pthread_mutex_lock死鎖引起的程式停止響應的方法,到此本文已經結束。

====================================================================================

本文已經結束了,但是再工作中遇到過導致pthread_mutex_lock引起死鎖的場景,這裡記錄下,符合上面死鎖的一個示例。

工作中遇到的是Semaphore訊號量和pthread_mutex_lock共同作用導致的死鎖,表面上看是訊號量sem和互斥量mutex,主要還是mutex的使用不合理導致的死鎖。

導致死鎖的程式碼結構如下所示:

void fun1()

{

m_mutex.enter(); 

........

m_sem.pend();

........

m_mutex.leave();

}

void fun2()

{

m_mutex.enter()

..........

m_sem.post();

..........

m_mutex.leave();

}

如下流程可以復現死鎖:

fun1()執行m_mutex.enter();成功

fun1()接著執行m_sem.pend(),此時fun1會pend停在此行(等待fun2()的訊號量post)

fun2()執行m_mutex.enter();,此時fun2執行enter()來獲取mutex(fun2必須停在此行等到fun1執行leave()後才能執行下行程式碼m_sem.post())

fun2()無法執行m_sem.post()

由上可知:fun1()等待fun2()進行訊號量執行post後才能釋放m_mutex,但是fun2需要獲取到m_mutex才能進行訊號量post.

推理可知fun1需要fun2的m_sem.post()才會m_mutex.leave(),但是fun2()需要fun1的m_mutex.leave()才能m_sem.post(),所以出現了fun1和fun2的死鎖問題。

仔細研究可知,fun1使用互斥訊號量m_mutex不當所致:沒有及時釋放m_mutex所致。

一種修改方法:

viod fun1()

{

m_mutex.enter();

........

m_mutex.leave();//及時釋放m_mutex,以防其他介面獲取鎖時導致死鎖

m_sem.pend();

m_mutex.enter();

.......

m_mutex.leave();

}

void fun2()

{

m_mutex.enter()

..........

m_sem.post();

..........

m_mutex.leave();

}

ps:可能還有其他方法。

《完》