在 Linux 中,行程與執行緒在核心層級皆以 task_struct 表示,均由 do_fork 建立,僅參數不同。若行程只有一條執行緒,信號自然發給該執行緒;但當行程擁有多條執行緒時,信號處理函數屬於行程資源,信號究竟會遞送給哪一條執行緒?本文透過三個實驗探討此問題。
gcc -g -W -Wall -Wextra -o mytest main.c -lpthread程式碼重點:主執行緒註冊 SIGQUIT 處理器,子執行緒註冊 SIGINT 處理器。兩執行緒皆進入 pause() 等待信號。
// 主執行緒
sigaction(SIGQUIT, &sa, 0);
// 子執行緒
sigaction(SIGINT, &sa, 0);
結果:無論在終端輸入 Ctrl-C (SIGINT) 或 Ctrl-\\ (SIGQUIT),信號**皆發送給主執行緒** (TID 2536)。
^CC
2536 recv signal: Interrupt
I'm Zuro 2536
^\
2536 recv signal: Quit
I'm Zuro 2536
修改:主執行緒註冊 SIGSEGV 處理器;子執行緒故意解參考空指標產生 SIGSEGV。
結果:信號**發送給產生錯誤的子執行緒**,而非主執行緒。
修改:主執行緒註冊 SIGQUIT 處理器;子執行緒呼叫 raise(SIGQUIT) 產生信號。
結果:信號**發送給呼叫 raise 的子執行緒**。
若信號由本行程產生 (如
raise、硬體例外),則遞送給產生該信號的執行緒;
若信號由其他行程產生 (如終端、kill),則遞送給主執行緒。
三個實驗的完整 main.c 原始碼已包含於原文中,關鍵差異在於 sigaction 註冊的信號與信號產生方式。讀者可自行編譯驗證。