承接上文,signal() 函數存在語義不一致、無法阻塞信號等缺陷。本篇介紹 POSIX 標準的 sigaction(),它提供了更健壯、可移植的信號處理配置機制。
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
SIGALRM、SIGUSR1)。struct sigaction*)。NULL。errno。struct sigaction {
void (*sa_handler)(int); // 普通信號處理函數
sigset_t sa_mask; // 信號屏蔽集,處理函數執行期間自動阻塞的信號
int sa_flags; // 標誌位,控制行為(如 SA_RESTART, SA_SIGINFO)
void (*sa_sigaction)(int, siginfo_t *, void *); // 擴展處理函數(SA_SIGINFO 時使用)
};
關鍵優勢:sa_mask 允許指定在信號處理函數執行期間臨時阻塞的信號集,避免嵌套中斷;sa_flags 提供豐富行為控制(如重啟被中斷的系統調用)。
#include
#include
#include
#include
void sig_receive(int signo) {
printf("received signal: %d\n", signo);
printf("signal processing in PID: %d\n", getpid());
// 實際業務邏輯...
}
int main() {
pid_t pid;
int stat_value;
struct sigaction act = {0};
// 1. 配置信號處理函數
act.sa_handler = sig_receive;
// 2. 設置信號屏蔽集為空(處理期間不阻塞額外信號)
sigemptyset(&act.sa_mask);
// 3. 設置標誌位(0 表示默認行為)
act.sa_flags = 0;
// 4. 註冊 SIGALRM 的處理動作
if (sigaction(SIGALRM, &act, NULL) == -1) {
perror("sigaction failed");
return 1;
}
// 5. 創建子進程
pid = fork();
if (pid > 0) { // 父進程
printf("PID: %d -> Father Process send signal\n", getpid());
kill(pid, SIGALRM); // 發送信號給子進程
} else if (pid == 0) { // 子進程
printf("PID: %d -> Son Process receive signal\n", getpid());
// 子進程這裡可執行其他任務,信號到達時會自動調用 sig_receive
} else {
perror("fork failed");
return 1;
}
// 父進程等待子進程結束
pid_t pid_res = wait(&stat_value);
if (pid_res > 0) {
printf("Son process finished: PID = %d\n", pid_res);
}
return 0;
}
PID: 4468 -> Father Process send signal
received signal: 14
signal processing in PID: 4469
PID: 4469 -> Son Process receive signal
Son process finished: PID = 4469
sa_mask 確保處理函數執行期間不被同類或指定信號中斷,避免競態條件。signal() 在 BSD/System V 中語義不同。SA_SIGINFO 獲取詳細信號上下文(siginfo_t、ucontext_t)。SA_RESTART 自動重啟被信號中斷的慢系統調用(如 read、wait)。sigaction() 替代 signal()。write、_exit、sigaction 本身等),避免調用 printf、malloc 等非安全函數(示例中為演示方便使用了 printf)。sa_mask 防止信號嵌套。sigemptyset()/sigaddset()/sigdelset() 操作信號集。掌握 sigaction 是編寫健壯 Linux 系統程序的關鍵一步。下一篇我們將進入管道通信的詳細實戰,敬請期待!