訊號分類:不可靠 vs 可靠 (實時)
| 類別 | 編號範圍 | 別名 | 排隊支援 | 丟失可能 | 典型用途 |
| 不可靠訊號 | 1~31 | 非實時訊號 | ❌ 不支援排隊 | ✅ 同一訊號多次發送,進程僅收到一次 | 終端控制 (SIGINT, SIGTSTP)、錯誤通知 (SIGSEGV, SIGFPE)、進程管理 (SIGCHLD, SIGKILL) |
| 可靠訊號 | 32~64 | 實時訊號 (RT) | ✅ 支援排隊 | ❌ 發多少次收多少次 | 使用者自定義通知、高併發事件傳遞、POSIX 計時器到期 |
終端查看完整列表:kill -l
常見不可靠訊號速查表 (節錄)
| 編號 | 名稱 | 預設動作 | 典型來源 |
| 1 | SIGHUP | 終止 | 終端斷開、nohup 忽略 |
| 2 | SIGINT | 終止 | Ctrl+C |
| 3 | SIGQUIT | 核心轉儲 | Ctrl+\ |
| 9 | SIGKILL | 終止 (不可捕捉/阻塞/忽略) | kill -9 |
| 11 | SIGSEGV | 核心轉儲 | 非法記憶體存取 |
| 13 | SIGPIPE | 終止 | 管道無讀端寫入 |
| 14 | SIGALRM | 終止 | alarm(), setitimer() |
| 15 | SIGTERM | 終止 | kill 預設、系統關機 |
| 17 | SIGCHLD | 忽略 | 子進程結束 |
| 19 | SIGSTOP | 停止 (不可捕捉/阻塞/忽略) | kill -STOP |
| 20 | SIGTSTP | 停止 | Ctrl+Z |
訊號產生來源
- 硬體:使用者輸入 (Ctrl+C → SIGINT)、CPU 異常 (除零 → SIGFPE、非法存取 → SIGSEGV)。
- 軟體 (系統呼叫):
kill(), raise(), sigqueue() (可攜帶資料), alarm(), setitimer(), abort(), pthread_kill()。
訊號安裝:signal() vs sigaction()
// 過時、不可攜帶資料、不可靠訊號語意不一
void (*signal(int signum, void (*handler)(int)))(int);
// 標準、支援資料傳遞、可控制阻塞集合、旗標豐富
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
sigaction 結構體關鍵欄位
struct sigaction {
void (*sa_handler)(int); // 傳統處理函式
void (*sa_sigaction)(int, siginfo_t*, void*); // SA_SIGINFO 時使用,可取得 siginfo_t
sigset_t sa_mask; // 處理函式執行期間額外阻塞的訊號集
int sa_flags; // 行為旗標組合
void (*sa_restorer)(void); // 內部用,忽略
};
sa_flags 常用旗標
SA_RESTART:被訊號中斷的系統呼叫自動重啟 (避免 EINTR)。
SA_NOCLDSTOP:子進程停止/繼續不產生 SIGCHLD。
SA_NOCLDWAIT:子進程結束不產生 SIGCHLD,且不成為殭屍進程。
SA_SIGINFO:啟用 sa_sigaction,可取得 siginfo_t (發送者 PID/UID、訊號值、地址等)。
SA_NODEFER:處理期間不自動阻塞自身訊號 (預設會阻塞)。
SA_RESETHAND:處理一次後恢復預設動作。
訊號集操作函式
sigemptyset(set) // 清空
sigfillset(set) // 填滿所有訊號
sigaddset(set, sig) // 加入
sigdelset(set, sig) // 刪除
sigismember(set, sig) // 判斷
阻塞/解除阻塞/待處理訊號查詢
sigprocmask(SIG_BLOCK, &set, &old) // 加入阻塞集
sigprocmask(SIG_UNBLOCK, &set, &old) // 移除阻塞集
sigprocmask(SIG_SETMASK, &set, &old) // 設定阻塞集
sigpending(&set) // 取得已送達但被阻塞的訊號集
sigsuspend(&mask) // 原子性:替換阻塞集並暫停,收到訊號恢復
核心處理流程與時機
- 訊號送達 → 掛入進程
task_struct->pending (不可靠訊號去重,可靠訊號入隊)。
- 進程從核心態返回使用者態時 (系統呼叫返回、中斷返回、排程器切換),檢查
pending & ~blocked。
- 若有未阻塞待處理訊號:
a. 依 sa_mask 設定暫時阻塞集。
b. 切換到使用者棧,呼叫處理函式 (sa_handler 或 sa_sigaction)。
c. 返回核心態,恢復原阻塞集,移除該訊號 pending 位。
d. 繼續執行使用者代碼。
關鍵點:訊號處理只發生在使用者態,核心態不處理訊號。sigprocmask 修改阻塞集後,下一次核心→使用者態轉換才生效。
實戰範例:使用 sigaction + sigqueue 傳遞資料
// 接收端
void handler(int sig, siginfo_t *info, void *ctx) {
printf("Received value: %d from PID %d\n", info->si_value.sival_int, info->si_pid);
}
struct sigaction act = {0};
act.sa_sigaction = handler;
act.sa_flags = SA_SIGINFO;
sigemptyset(&act.sa_mask);
sigaction(SIGRTMIN, &act, NULL);
// 發送端
union sigval val;
val.sival_int = 12345;
sigqueue(target_pid, SIGRTMIN, val);