| 旗標 | 效果 |
SA_RESTART | 被信號中斷的系統呼叫自動重啟(如 read、accept) |
SA_SIGINFO | 使用 sa_sigaction 取得 siginfo_t 詳細資訊 |
SA_NOCLDSTOP | 子進程停止不產生 SIGCHLD |
SA_NOCLDWAIT | 子進程結束不產生殭屍、不發 SIGCHLD |
SA_NODEFER | 處理期間不自動阻塞同一信號(預設會阻塞) |
SA_RESETHAND | 處理後重置為預設動作(一次性) |
SA_ONSTACK | 使用替代信號堆疊(sigaltstack) |
sigaction() 完整範例
#include
#include
#include
void handler(int sig) {
printf("Received signal: %s\n", strsignal(sig));
}
int main() {
struct sigaction act = {0};
sigemptyset(&act.sa_mask); // 清空遮罩
act.sa_handler = handler;
act.sa_flags = SA_RESTART; // 系統呼叫自動重啟
if (sigaction(SIGUSR1, &act, NULL) == -1) {
perror("sigaction");
return 1;
}
while (1) pause(); // 等待信號
return 0;
}
三、信號遮罩與阻塞控制
// 信號集操作
sigset_t set;
sigemptyset(&set); // 清空
sigfillset(&set); // 全部
sigaddset(&set, SIGUSR1); // 加入
sigdelset(&set, SIGUSR1); // 移除
sigismember(&set, SIGUSR1); // 檢查
// 進程信號遮罩(阻塞/解除阻塞)
sigprocmask(SIG_BLOCK, &set, &oldset); // 阻塞
sigprocmask(SIG_UNBLOCK, &set, &oldset); // 解除
sigprocmask(SIG_SETMASK, &set, &oldset); // 設定
// 臨時解除阻塞等待信號
sigsuspend(&mask); // 原子性:解除阻塞 + 等待 + 恢復阻塞
四、非同步信號安全函數
信號處理函數中只能呼叫 async-signal-safe 函數,否則導致未定義行為(死鎖、資料損毀)。
常見安全函數(man 7 signal-safety)
_exit, write, read, open, close, sigaction, sigprocmask,
sigemptyset, sigfillset, sigaddset, sigdelset, sigismember,
kill, raise, alarm, pause, sleep, getpid, getuid, time,
strsignal, psignal(部分配置)
⚠️ 絕對不可呼叫
printf, malloc, free, fopen, fclose, pthread_mutex_lock,
pthread_cond_signal, syslog, getpwnam, inet_ntoa,
所有非重入函數、使用全域狀態的函數
正確模式:信號處理器只設旗標,主迴圈處理邏輯
volatile sig_atomic_t got_sigusr1 = 0;
void handler(int sig) {
got_sigusr1 = 1; // 僅設旗標
}
int main() {
// 安裝 handler...
while (1) {
if (got_sigusr1) {
got_sigusr1 = 0;
// 安全執行複雜邏輯:printf、malloc、DB 操作等
do_complex_work();
}
// 其他工作...
}
}
五、多執行緒與信號
- 信號送達給進程,由核心選擇未阻塞該信號的任一執行緒處理
- 建立執行緒時繼承進程信號遮罩(
pthread_sigmask 可單獨設定)
- 典型模式:專用一條執行緒
sigwait() 同步處理信號
// 專用信號處理執行緒
void* signal_thread(void* arg) {
sigset_t set;
sigfillset(&set);
pthread_sigmask(SIG_BLOCK, &set, NULL); // 阻塞所有信號
while (1) {
int sig;
sigwait(&set, &sig); // 同步等待
handle_signal(sig); // 安全處理
}
}
int main() {
sigset_t set;
sigfillset(&set);
pthread_sigmask(SIG_BLOCK, &set, NULL); // 主執行緒阻塞所有
pthread_t tid;
pthread_create(&tid, NULL, signal_thread, NULL);
// 主執行緒專心業務邏輯
}
六、SA_RESTART 與系統呼叫中斷
| 系統呼叫 | 無 SA_RESTART | 有 SA_RESTART |
read, write | 回傳 -1, errno=EINTR | 自動重啟 |
accept, recv | 回傳 -1, errno=EINTR | 自動重啟 |
select, poll | 回傳 -1, errno=EINTR | 自動重啟 |
sleep | 回傳剩餘秒數 | 自動重啟 |
wait, waitpid | 回傳 -1, errno=EINTR | 自動重啟 |
connect | 回傳 -1, errno=EINTR | 不重啟(連線建立不可重入) |
七、實戰模式總結
- 一律使用
sigaction(),設定 SA_RESTART
- 處理器極簡:僅設
volatile sig_atomic_t 旗標或 write() 到 self-pipe
- 複雜邏輯在主迴圈 輪詢旗標或
select/poll/epoll 監聽 self-pipe
- 多執行緒:專用
sigwait() 執行緒,主執行緒阻塞所有信號
- 啟動時 先
sigfillset + pthread_sigmask(SIG_BLOCK),再建立執行緒
精通信號機制,是編寫健壯 Linux 系統程式、高效能網路伺服器、容器化應用的核心基本功。