信號是 Linux 進程間通訊最基礎也最關鍵的機制之一。本文系統梳理信號集操作、阻塞與忽略的本質區別、sigprocmask 動態修改信號掩碼、sigaction 註冊處理函數的完整語義,為開發高可靠系統軟體奠定理論基礎。
每個進程擁有獨立的 sigset_t 類型信號掩碼,標記哪些信號被阻塞。信號被阻塞時,內核暫緩投遞,不影響進程當前行為;解除阻塞後信號才會被遞送。子進程 fork() 時會繼承父進程的信號掩碼。
| 維度 | 阻塞 | 忽略 |
|---|---|---|
| 信號投遞 | 暫緩,解除阻塞後投遞 | 立即投遞,但進程丟棄不處理 |
| 進程行為 | 不受影響 | 不處理,相當於無視 |
| 典型場景 | 臨界區保護、原子操作 | 不關心的信號(如 SIGCHLD 防殭屍) |
int sigemptyset(sigset_t *set); // 清空集合
int sigfillset(sigset_t *set); // 填入所有信號
int sigaddset(sigset_t *set, int signum); // 加入指定信號
int sigdelset(sigset_t *set, int signum); // 移除指定信號
int sigismember(const sigset_t *set, int signum); // 判斷是否包含
常用模式:sigemptyset() 初始化 → sigaddset() 逐個加入;或 sigfillset() 全加入 → sigdelset() 排除特定信號。
#include <signal.h>
int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict old);
how 三種模式:
SIG_BLOCK:將 set 加入當前阻塞集(聯集)SIG_UNBLOCK:從當前阻塞集移除 set(差集)SIG_SETMASK:直接以 set 替換當前阻塞集set 為 NULL 時不修改,僅查詢;old 為 NULL 時不返回舊掩碼。
struct sigaction {
void (*sa_handler)(int); // 舊版處理函數
void (*sa_sigaction)(int, siginfo_t*, void*); // 新版處理函數
sigset_t sa_mask; // 處理期間額外阻塞的信號集
int sa_flags; // 行為標誌位組合
void (*sa_restorer)(void); // 保留字段
};
SA_SIGINFO 標誌,新版可獲取 siginfo_t 詳細信號上下文SA_RESETHAND:處理一次後自動重置為預設動作SA_NODEFER:允許遞歸處理同一信號(忽略 sa_mask)SA_RESTART:系統調用被中斷後自動重啟(強烈建議啟用)SA_SIGINFO:啟用新版處理函數sigprocmask 是「全程阻塞」,修改後持續生效直到再次修改;sa_mask 僅在「處理特定信號的當下」生效,屬於臨時性阻塞。兩者配合使用可構建精細的信號同步策略。
實戰建議:多線程程序中信號處理極其複雜,建議採用「專用信號處理線程 + sigwaitinfo()」模型,避免在主業務邏輯中直接處理異步信號。