演示如何使用 sigprocmask 暫時阻塞特定信號(SIGQUIT),並透過 sigpending 檢查未決信號,驗證信號阻塞期間產生的信號不會丟失,解除阻塞後立即被處理。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
void error_quit(const char *str) {
fprintf(stderr, "%s\n", str);
exit(1);
}
void sig_quit(int signo) {
printf("caught SIGQUIT\n");
if (signal(SIGQUIT, SIG_DFL) == SIG_ERR)
error_quit("can't reset SIGQUIT");
}
int main(void) {
sigset_t newmask, oldmask, pendmask;
// 設置 SIGQUIT 處理函數
if (signal(SIGQUIT, sig_quit) == SIG_ERR)
error_quit("can't catch SIGQUIT");
// 初始化並添加 SIGQUIT 到新屏蔽集
sigemptyset(&newmask);
sigaddset(&newmask, SIGQUIT);
// 阻塞 SIGQUIT,保存舊屏蔽集
if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0)
error_quit("SIG_BLOCK error");
sleep(5); // 此期間按 Ctrl+\ 產生 SIGQUIT,將變為未決
// 檢查未決信號
if (sigpending(&pendmask) < 0)
error_quit("sigpending error");
if (sigismember(&pendmask, SIGQUIT))
printf("\nSIGQUIT pending\n");
// 解除阻塞,恢復原屏蔽集
if (sigprocmask(SIG_SETMASK, &oldmask, NULL) < 0)
error_quit("SIG_SETMASK error");
printf("SIGQUIT unblocked\n");
sleep(5); // 此期間再次按 Ctrl+\ 將觸發預設動作(退出)
return 0;
}
$ ./temp
^\ # 第 5 秒內按下 Ctrl+\
SIGQUIT pending # sleep 返回,檢測到未決
caught SIGQUIT # sigprocmask 解除阻塞前,處理函數已執行
SIGQUIT unblocked
^\ # 再次按下,觸發預設動作退出
若在阻塞期間連按多次 Ctrl+\,解除阻塞後僅執行一次處理函數,驗證標準信號不排隊特性。
sigprocmask 解除阻塞的動作在函數返回之前已完成,未決信號隨即被處理。「未決」是狀態,「阻塞」是開關動作。APUE 例題中,
sleep期間信號被阻塞,故處於未決狀態;sigprocmask關閉阻塞開關瞬間,未決信號立即得到處理,且發生在sigprocmask返回之前。