sigfillset
函數詳解
一、函數概念
sigfillset()
是 POSIX 信號處理中的核心函數,用于初始化并填充一個信號集,使其包含當前系統支持的所有信號。它是操作信號屏蔽字(signal mask)的基礎工具,常與 sigprocmask()
、sigsuspend()
等函數配合使用。
關鍵特性:
- 初始化信號集:將
sigset_t
類型變量設為包含所有信號 - 原子操作:保證線程安全
- 不可阻塞信號:對
SIGKILL
和SIGSTOP
無效(這兩個信號永遠不可阻塞)
二、函數原型和頭文件
#include <signal.h>int sigfillset(sigset_t *set);
參數:
set
:指向要填充的信號集的指針
返回值:
- 成功:返回 0
- 失敗:返回 -1 并設置
errno
(通常是EFAULT
,表示無效指針)
三、信號集(sigset_t
)詳解
信號集是用于表示一組信號的數據類型,本質是位掩碼(bitmask)。在 Linux 中通常定義為:
typedef struct {unsigned long sig[_NSIG_WORDS];
} sigset_t;
信號范圍:
- 標準信號:1~31(如
SIGINT=2
,SIGSEGV=11
) - 實時信號:32~64(
SIGRTMIN
到SIGRTMAX
)
使用
kill -l
命令可查看系統支持的信號列表
四、使用場景
1. 阻塞所有信號
sigset_t all_signals;
sigfillset(&all_signals); // 填充所有信號// 設置進程信號屏蔽字
sigprocmask(SIG_SETMASK, &all_signals, NULL);
2. 安全臨界區保護
void critical_section() {sigset_t old_set, new_set;// 準備阻塞所有信號sigfillset(&new_set);// 進入臨界區前阻塞信號sigprocmask(SIG_SETMASK, &new_set, &old_set);/* 臨界區代碼(不會被任何信號中斷) */// 恢復原始信號屏蔽sigprocmask(SIG_SETMASK, &old_set, NULL);
}
3. 等待特定信號
sigset_t mask, original_mask;
sigfillset(&mask); // 包含所有信號
sigdelset(&mask, SIGUSR1); // 刪除 SIGUSR1// 阻塞除 SIGUSR1 外的所有信號
sigprocmask(SIG_SETMASK, &mask, &original_mask);// 等待 SIGUSR1 信號
sigsuspend(&mask);// 恢復原始信號屏蔽
sigprocmask(SIG_SETMASK, &original_mask, NULL);
五、完整使用示例
示例:安全信號處理框架
#include <signal.h>
#include <stdio.h>
#include <unistd.h>volatile sig_atomic_t flag = 0;void handler(int sig) {flag = 1; // 僅設置標志(異步安全)
}int main() {// 配置信號處理struct sigaction sa;sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sa.sa_flags = SA_RESTART;sigaction(SIGINT, &sa, NULL);// 準備信號集sigset_t all_signals, wait_mask;sigfillset(&all_signals); // 包含所有信號sigemptyset(&wait_mask); // 空信號集sigaddset(&wait_mask, SIGINT); // 只關注SIGINT// 阻塞除SIGINT外的所有信號sigset_t old_mask;sigprocmask(SIG_SETMASK, &all_signals, &old_mask);sigdelset(&all_signals, SIGINT); // 允許SIGINT傳遞while(1) {// 安全等待信號(原子操作)sigsuspend(&wait_mask);if(flag) {printf("Processing signal...\n");/* 安全處理邏輯(非異步安全函數放這里) */flag = 0;}}// 恢復原始信號屏蔽(實際不會執行到這里)sigprocmask(SIG_SETMASK, &old_mask, NULL);return 0;
}
六、相關函數對比
函數 | 功能 | 常見使用場景 |
---|---|---|
sigfillset() | 填充所有信號到信號集 | 初始化全局屏蔽 |
sigemptyset() | 清空信號集 | 準備添加特定信號 |
sigaddset() | 添加單個信號到信號集 | 自定義屏蔽組合 |
sigdelset() | 從信號集刪除單個信號 | 允許特定信號通過 |
sigismember() | 檢查信號是否在集合中 | 信號狀態判斷 |
sigprocmask() | 設置進程信號屏蔽字 | 臨界區保護/信號阻塞 |
sigsuspend() | 臨時替換信號掩碼并等待信號 | 原子等待操作 |
七、重要注意事項
-
信號集生命周期:
// 錯誤!未初始化信號集 sigset_t set; sigprocmask(SIG_SETMASK, &set, NULL); // 未定義行為// 正確:必須顯式初始化 sigset_t set; sigfillset(&set); // 或 sigemptyset(&set);
-
不可阻塞信號:
sigset_t set; sigfillset(&set); sigprocmask(SIG_SETMASK, &set, NULL);// 以下信號仍能終止進程 kill(getpid(), SIGKILL); // 始終有效 kill(getpid(), SIGSTOP); // 始終有效
-
線程安全:
- 在多線程環境中,使用
pthread_sigmask()
替代sigprocmask()
pthread_sigmask(SIG_SETMASK, &set, NULL);
- 在多線程環境中,使用
-
與
sigaction
協作:struct sigaction sa; sa.sa_handler = handler; sigfillset(&sa.sa_mask); // 執行處理函數時阻塞所有其他信號 sa.sa_flags = 0;
八、錯誤處理
sigset_t signal_set;if (sigfillset(&signal_set) == -1) {perror("sigfillset failed");switch(errno) {case EFAULT:fprintf(stderr, "Invalid memory address\n");break;default:fprintf(stderr, "Unknown error\n");}exit(EXIT_FAILURE);
}
總結
sigfillset()
是 Linux 信號處理的基礎構建塊:
- 核心作用:快速初始化包含所有信號的信號集
- 典型應用:
- 創建全局信號屏蔽
- 實現安全臨界區
- 配合
sigsuspend()
實現原子等待
- 最佳實踐:
- 總是顯式初始化信號集
- 結合
sigdelset()
實現精細控制 - 在多線程程序中使用線程安全版本
掌握 sigfillset()
及其相關函數,是編寫健壯的信號處理代碼的關鍵一步。通過合理控制信號屏蔽,可以有效防止信號競爭條件和不可預知的中斷行為。