一次測試環境發生了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。所以只能修改訊號處理的方案。
訊號處理的方式除了非同步使用方式還有同步使用方式。同步訊號處理
一次測試環境發生了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。所以只能修改訊號處理的方案。
訊號處理的方式除了非同步使用方式還有同步使用方式。同步訊號處理