多執行緒伺服器中,定時器訊號處理函數若呼叫非重入函數(如 malloc/free),極易造成死鎖。本文分析實際案例,提出「標誌位延遲處理」模式徹底規避風險。
主執行緒正在 free() 釋放記憶體(持有 malloc 內部鎖),定時器訊號(SIGALRM)中斷執行,進入訊號處理函數 CTimerManager::Process(),其中再次呼叫 pop_front() 觸發 free(),導致雙重嘗試獲取同一把鎖 → 死鎖。
#0 __kernel_vsyscall()
#1 __lll_mutex_lock_wait() // 等待 malloc 鎖
#2 _L_lock_14080()
#3 free() // 訊號處理函數中呼叫
#4 operator delete()
#5 std::list::pop_front() // CTimerManager::Process()
#6 Process(nSigNo=14) // 訊號處理入口
#7
#8 _int_free() // 主執行緒原本在這裡
#9 free()
...
free(),難以手動規避。malloc/free 不在列表中。將訊號處理函數簡化為「設定原子旗標」,實際邏輯移至主迴輪詢執行:
void signal_handler() { dosomething(); } // 直接在訊號中處理
while (1) { server_loop(); }
volatile sig_atomic_t g_alarm = 0; // 原子旗標
void signal_handler() { g_alarm = 1; } // 僅設旗標,極快返回
while (1) {
server_loop();
if (g_alarm) {
dosomething(); // 在主執行緒安全上下文執行
g_alarm = 0;
}
}
signalfd + epoll:Linux 2.6.22+ 支援將訊號轉為檔案描述符,納入事件迴路,完全避免訊號處理函數。timer_create(SIGEV_THREAD) 讓內核在新執行緒回調,擁有完整函數庫權限。boost::lockfree::spsc_queue)。sem_post。volatile sig_atomic_t 或使用 std::atomic<int> 確保可見性與原子性。此模式不僅解決死鎖,也簡化併發推理,是高可靠性 Linux 伺服器開發的基礎範式。