在 Unix/Linux 系統編程中,管理信號處理行為涉及以下核心概念和模型,它們共同構成了信號處理的框架:
1. 信號(Signal)模型
- 軟件中斷:信號是異步事件通知機制,類比硬件中斷
- 預定義類型:
SIGINT
(Ctrl+C)、SIGTERM
(終止請求)、SIGSEGV
(段錯誤) 等標準信號 - 生命周期:
- 生成(Generation):事件觸發信號產生
- 遞送(Delivery):內核將信號傳遞給目標進程
- 處理(Handling):進程執行注冊的處理動作
2. 信號處理行為控制
行為類型 | 說明 | 設置方式 |
---|---|---|
默認行為 | 系統預定義行為(終止/忽略/暫停) | SIG_DFL |
忽略信號 | 丟棄信號不做任何響應 | SIG_IGN |
自定義處理函數 | 用戶注冊的信號處理例程 | 函數指針 |
3. 關鍵控制機制
(1) 信號阻塞(Blocking)
- 目的:臨時阻止信號遞送
- 實現:
sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGINT); sigprocmask(SIG_BLOCK, &mask, NULL); // 阻塞SIGINT
- 特點:
- 被阻塞的信號處于**掛起(Pending)**狀態
sigpending()
可獲取掛起信號集- 解除阻塞后立即遞送
(2) 信號屏蔽(Masking)
- 執行處理函數時自動生效:
- 當前處理的信號自動被屏蔽(除非設置
SA_NODEFER
) - 通過
sa_mask
指定額外屏蔽的信號集
- 當前處理的信號自動被屏蔽(除非設置
- 作用:防止信號處理函數被重入
4. 信號處理模型
(1) 單次處理模型
struct sigaction sa = {.sa_handler = handler,.sa_flags = SA_RESETHAND // 處理一次后恢復默認行為
};
- 特點:類似傳統
signal()
的行為 - 風險:信號再次發生時可能觸發默認行為(如終止進程)
(2) 持久處理模型
struct sigaction sa = {.sa_handler = handler,.sa_flags = 0 // 持續有效
};
- 特點:處理函數保持激活狀態
- 最佳實踐:配合信號阻塞使用
(3) 實時信號處理模型
struct sigaction sa = {.sa_sigaction = rt_handler,.sa_flags = SA_SIGINFO | SA_RESTART
};
- 特點:
- 支持信號排隊(避免丟失)
- 可攜帶附加信息(發送者PID、錯誤地址等)
- 使用
sigqueue()
發送:
union sigval value = {.sival_int = 42}; sigqueue(pid, SIGRTMIN+1, value);
5. 關鍵系統調用
系統調用 | 用途 |
---|---|
sigaction() | 注冊信號處理行為(核心接口) |
sigprocmask() | 控制進程信號屏蔽集 |
sigsuspend() | 原子操作:設置屏蔽集 + 等待信號 |
kill() /raise() | 發送信號(跨進程/自身) |
sigaltstack() | 設置備選信號棧(處理棧溢出信號) |
6. 特殊處理場景
(1) 系統調用中斷處理
- 問題:慢速系統調用(如
read()
)被信號中斷 - 解決方案:
struct sigaction sa = {.sa_handler = handler,.sa_flags = SA_RESTART // 自動重啟被中斷的系統調用 };
(2) 信號競爭處理
- 臨界區保護模式:
sigset_t new_mask, old_mask; sigemptyset(&new_mask); sigaddset(&new_mask, SIGINT);// 進入臨界區前阻塞信號 sigprocmask(SIG_BLOCK, &new_mask, &old_mask);/* 臨界區代碼(不會被SIGINT中斷) */// 等待可能發生的信號 sigsuspend(&old_mask);// 恢復原始屏蔽集 sigprocmask(SIG_SETMASK, &old_mask, NULL);
(3) 子進程終止處理
void sigchld_handler(int sig) {while (waitpid(-1, NULL, WNOHANG) > 0); // 非阻塞回收所有僵尸進程
}// 注冊處理
struct sigaction sa = {.sa_handler = sigchld_handler,.sa_flags = SA_RESTART | SA_NOCLDSTOP
};
sigaction(SIGCHLD, &sa, NULL);
7. 安全編程模型
-
異步信號安全(Async-signal-safe):
- 信號處理函數中只能調用異步安全函數(如
write()
,_exit()
) - 禁止調用非可重入函數(
malloc
,printf
等)
- 信號處理函數中只能調用異步安全函數(如
-
自包含狀態:
volatile sig_atomic_t flag = 0; // 信號安全標志void handler(int sig) {flag = 1; // 僅設置標志,主循環中處理 }
-
備選信號棧:
stack_t ss = {.ss_sp = malloc(SIGSTKSZ), .ss_size = SIGSTKSZ }; sigaltstack(&ss, NULL); // 設置備選棧struct sigaction sa = {.sa_handler = handler,.sa_flags = SA_ONSTACK // 使用備選棧 };
概念關系圖
+---------------------+
| 信號產生源 | (硬件/內核/進程)
+----------+----------+| 生成信號v
+---------------------+
| 內核信號隊列 | (實時信號排隊)
+----------+----------+| 遞送決策v
+---------------------+ 阻塞? +----------+
| 進程信號屏蔽集 +--------->| 掛起狀態 |
+----------+----------+ +----------+| 未阻塞?v
+---------------------+
| 信號處理分發 |
| +----------------+ |
| | 默認行為處理 | |
| | 忽略信號 | |
| | 自定義處理函數 | |
| +----------------+ |
+---------------------+|v
+---------------------+
| 處理函數執行環境 |
| - 自動信號屏蔽 |
| - 備選信號棧 |
| - 中斷系統調用 |
+---------------------+
這些核心概念共同構成了 Unix/Linux 信號處理的完整模型,開發者需要理解其交互機制才能編寫出健壯可靠的信號處理代碼。