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()
及其相关函数,是编写健壮的信号处理代码的关键一步。通过合理控制信号屏蔽,可以有效防止信号竞争条件和不可预知的中断行为。