信號 是 Linux 進程間異步通信的核心機制,亦是內核通知進程發生異步事件 (硬體異常、終端操作、定時器、用戶請求) 的唯一途徑。本文系統梳理信號基本概念、完整信號表、處理流程、核心系統調用與實戰編程模式。
| 動作 | 描述 | 系統調用 | ||
|---|---|---|---|---|
| 捕獲 | 註冊處理函數,發生時執行用戶代碼 | signal(), sigaction() | ||
| 忽略 | 丟棄信號,如未發生 (SIGKILL/SIGSTOP 不可忽略) | signal(SIG_IGN) | ||
| 默認 | 內核預設動作 (Term/Core/Stop/Ignore) | signal(SIG_DFL) |
| 信號 | 值 | 默認動作 | 來源/含義 | 備註 |
|---|---|---|---|---|
| SIGHUP | 1 | Term | 終端掛起/控制進程終止 | 守護進程常用於重讀配置 |
| SIGINT | 2 | Term | 鍵盤中斷 | Ctrl+C |
| SIGQUIT | 3 | Core | 鍵盤退出鍵 | Ctrl+\ 產生 core dump |
| SIGILL | 4 | Core | 非法指令 | |
| SIGTRAP | 5 | Core | 斷點/跟蹤陷阱 | 調試器使用 |
| SIGABRT | 6 | Core | abort() 調用 | |
| SIGBUS | 7 | Core | 總線錯誤 (非對齊訪問) | |
| SIGFPE | 8 | Core | 浮點異常 (除零等) | |
| SIGKILL | 9 | Term | 無條件終止 | 不可捕獲/忽略/阻塞 |
| SIGUSR1 | 10 | Term | 用戶自定義 1 | 應用層協議 |
| SIGSEGV | 11 | Core | 無效內存引用 | 段錯誤 |
| SIGUSR2 | 12 | Term | 用戶自定義 2 | |
| SIGPIPE | 13 | Term | 管道破裂 (寫無讀端) | 網路編程常見 |
| SIGALRM | 14 | Term | alarm() 定時器 | |
| SIGTERM | 15 | Term | 優雅終止請求 | systemd stop 默認發送 |
| SIGSTKFLT | 16 | Term | 協處理器堆棧錯誤 | 較少見 |
| SIGCHLD | 17 | Ign | 子進程結束/停止 | 需 wait() 回收避免殭屍 |
| SIGCONT | 18 | Cont | 繼續執行 (被停止進程) | |
| SIGSTOP | 19 | Stop | 無條件停止 | 不可捕獲/忽略/阻塞 |
| SIGTSTP | 20 | Stop | 終端停止鍵 | Ctrl+Z |
| SIGTTIN | 21 | Stop | 後台進程讀終端 | |
| SIGTTOU | 22 | Stop | 後台進程寫終端 | |
| SIGURG | 23 | Ign | Socket 緊急數據 | 帶外數據 |
| SIGXCPU | 24 | Core | CPU 時間超限 | setrlimit(RLIMIT_CPU) |
| SIGXFSZ | 25 | Core | 文件大小超限 | setrlimit(RLIMIT_FSIZE) |
| SIGVTALRM | 26 | Term | 虛擬定時器 | setitimer(ITIMER_VIRTUAL) |
| SIGPROF | 27 | Term | 剖析定時器 | setitimer(ITIMER_PROF) |
| SIGWINCH | 28 | Ign | 終端窗口大小變化 | 終端程序處理 |
| SIGIO | 29 | Term | I/O 可用 (異步 I/O) | SIGPOLL 同義 |
| SIGPWR | 30 | Term | 電源故障 | UPS 通知 |
| SIGSYS | 31 | Core | 無效系統調用 | seccomp 觸發 |
| SIGRTMIN ~ SIGRTMAX | 32~64 | Term | 實時信號 (排隊、攜帶數據) | 應用層定義 |
動作代碼:Term=終止、Core=終止並產生核心轉儲、Stop=停止、Cont=繼續、Ign=忽略。
#include
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
struct sigaction {
void (*sa_handler)(int); // 普通處理函數
void (*sa_sigaction)(int, siginfo_t *, void *); // SA_SIGINFO 時使用
sigset_t sa_mask; // 處理期間自動阻塞的信號集
int sa_flags; // SA_RESTART, SA_SIGINFO, SA_NOCLDSTOP...
void (*sa_restorer)(void); // 內部使用,忽略
};
#include
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);
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
// how: SIG_BLOCK, SIG_UNBLOCK, SIG_SETMASK
int sigpending(sigset_t *set); // 讀取未決信號集
int kill(pid_t pid, int signum); // 進程/進程組
int raise(int signum); // 當前進程
int sigqueue(pid_t pid, int signum, const union sigval value); // 實時信號攜帶數據
int sigsuspend(const sigset_t *mask); // 原子地設置 mask 並掛起,捕獲信號返回
int sigwait(const sigset_t *set, int *sig); // 同步等待集合中任一信號 (多線程專用)
int sigwaitinfo(const sigset_t *set, siginfo_t *info); // 同步等待並獲取詳細信息
void handler(int sig) {
// 僅設置標誌,非 async-signal-safe 函數不可調用
volatile sig_atomic_t got_sigterm = 1;
}
int main() {
struct sigaction sa = {0};
sa.sa_handler = handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART; // 關鍵:避免慢速系統調用返回 EINTR
sigaction(SIGTERM, &sa, NULL);
sigaction(SIGINT, &sa, NULL);
while (!got_sigterm) {
// 主循環
pause(); // 或 sleep、select、epoll_wait
}
// 清理資源後退出
return 0;
}
void sigchld_handler(int sig) {
int saved_errno = errno;
while (waitpid(-1, NULL, WNOHANG) > 0) {} // 循環回收所有已終止子進程
errno = saved_errno;
}
sigaction(SIGCHLD, &(struct sigaction){.sa_handler=sigchld_handler, .sa_flags=SA_RESTART|SA_NOCLDSTOP}, NULL);
// 發送端
union sigval val;
val.sival_int = 42;
sigqueue(pid, SIGRTMIN+1, val);
// 接收端 (需 SA_SIGINFO)
void rt_handler(int sig, siginfo_t *info, void *ctx) {
printf("收到實時信號 %d, 數據: %d, 發送者 PID: %d\n", sig, info->si_value.sival_int, info->si_pid);
}
sigaction(SIGRTMIN+1, &(struct sigaction){.sa_sigaction=rt_handler, .sa_flags=SA_SIGINFO}, NULL);
僅調用 POSIX 保證安全的函數:`write`、`_exit`、`sigaction`、`sigprocmask`、`sigemptyset`、`sigaddset`、`sigdelset`、`sigismember`、`kill`、`raise`、`alarm`、`pause`、`sleep`、`abort`、`getpid`、`getppid` 等。**嚴禁調用 `printf`、`malloc`、`free`、`lock`、非重入庫函數**。
信號是 Linux 異步編程的基石。掌握 `sigaction` + `SA_RESTART` + `sigprocmask` + `sigsuspend/sigwaitinfo` 組合拳,遵循「處理函數極簡、僅設置標誌、主循環統一處理」原則,能構建健壯的服務器、守護進程、容器管理器等系統級軟體。實時信號 (SIGRT) 進一步解決了標準信號不排隊、無數據的限制,適用於高併發事件通知場景。