如何解決Linux下信號產生的死鎖?
一次測試環境發生了server死鎖,整個server的任務線程都被hang住。而死鎖的代碼就在我負責的程序日志部分中localtime_r函數調用處。
程序日記需要記錄打印日志的時間,而localtime_r函數就是用于將系統時間轉換為本地時間。同樣功能的函數還有localtime。兩個函數的區別是:localtime_r是thread-safe,其返回的結果存在由用戶提供的buffer中;而localtime返回的結果是指向static變量,多線程環境可被其他線程修改。localtime_r實現中有一把鎖,負責lock tzfile中的狀態變量,而server就在這里發生死鎖。
經過分析死鎖是由于發kill信號,信號處理函數引起的。原線程打印程序日志獲得localtime_r中需要的鎖后,kill信號觸發中斷處理,正好分配給該線程處理中斷。信號處理函數中再次打印日志,調用localtime_r的鎖時發生死鎖。
之前的信號處理方式為異步方式,同時信號處理函數中做了很多事情。之前大家一直關注線程安全,卻從來沒有注意過異步信號處理函數的安全性。這次最新版本由于還在開發中,大家調用了大量日志打印,增加了死鎖的概率才將這個問題暴露出來。
##Signal Handling and Nonreentrant Functions
信號處理函數不推薦做太多工作,如果調用函數需要是reentrant。reentrant可重新進入的,可以理解為一次調用發生后,不會對該函數的再次調用發生任何影響。即reentrant函數中不可以有static或global變量,不可以分配釋放內存,通常不可以使用修改用戶提供的對象,修改errno等等。具體可以看http://www.gnu.org/software/libc/manual/html_node/Nonreentrancy.html#Nonreentrancy
##解決信號處理帶來的死鎖 異步變同步
自己的第一直覺是既然信號處理函數不可以做太多工作,需要調用non-reentrant函數,那就把日志打印全部去掉好了。但發現,團隊項目的信號處理函數中做了大量工作,許多調試方法和調試信息通過kill信號獲得,而且這些調用基本都是non-reentrant。所以只能修改信號處理的方案。
信號處理的方式除了異步使用方式還有同步使用方式。同步信號處理