pthread_sigmask 可通过传入 nullptr 作为 set 参数、提供非空 oset 参数来获取当前线程信号掩码:int pthread_sigmask(SIG_SETMASK, nullptr, &oldmask);此时不修改掩码,仅读取到 oldmask 中,且 sigset_t 必须预先声明,无需初始化。

如何用 pthread_sigmask 获取当前线程的信号掩码
在 Linux 上,C++ 程序(严格说是 POSIX 线程)不能直接“获取进程级”信号掩码——信号掩码是线程级别的,每个线程有独立的 sigset_t。所谓“当前进程的信号掩码”实际指主线程(或调用线程)的掩码。
最常用、最可靠的方式是调用 pthread_sigmask 并传入 SIG_BLOCK 以外的操作(如 SIG_SETMASK 配合空集,或更推荐的 SIG_UNBLOCK 配合空集),但真正用于“读取”的是把第二个参数设为 nullptr,第三个参数指向你提供的 sigset_t*:
sigset_t oldmask;
int ret = pthread_sigmask(SIG_SETMASK, nullptr, &oldmask); // 注意:第二个参数为 nullptr
if (ret != 0) {
// 处理 errno,比如 EINVAL 或 EFAULT
}
这里 pthread_sigmask 的行为是:当 set == nullptr 时,只读取当前线程掩码到 oldset,不修改任何东西。
-
pthread_sigmask是线程安全的,比老式sigprocmask更适合多线程程序 - 必须先声明
sigset_t oldmask并确保其已初始化(sigemptyset(&oldmask)非必需,但读取前清零可避免未定义值干扰调试) - 如果在 signal handler 中调用,需注意异步信号安全 ——
pthread_sigmask不是 async-signal-safe 函数,禁止在 handler 里用
如何检查某个信号是否被屏蔽(例如 SIGINT)
拿到 sigset_t 后不能直接打印或比较,得用标准宏操作:
立即学习“C++免费学习笔记(深入)”;
sigset_t mask;
pthread_sigmask(SIG_SETMASK, nullptr, &mask);
if (sigismember(&mask, SIGINT)) {
// SIGINT 当前被屏蔽
}
sigismember 是唯一便携、标准的方式判断单个信号状态。别试图用位运算或 memcmp —— sigset_t 的内部布局是实现定义的,不同 libc(glibc vs musl)甚至不同架构下可能不同。
-
sigismember返回 1(在集合中)、0(不在)、-1(错误,检查errno) - 信号常量必须用
SIGxxx宏,不能硬写数字(如2代替SIGINT),否则移植性崩坏 - 注意:
SIGKILL和SIGSTOP永远无法被屏蔽,sigismember对它们返回 0 是正常现象
为什么不用 sigprocmask?
sigprocmask 只作用于调用线程,且在多线程程序中行为未定义(POSIX 明确标注为“should not be used”)。glibc 实现里它等价于 pthread_sigmask,但这是实现细节,不是标准保证。
- 在单线程程序里
sigprocmask能工作,但一旦链接了-lpthread或用了std::thread,就进入未定义行为区域 - 编译器或 sanitizer(如 ThreadSanitizer)可能直接报 warning 或 crash
- Clang/GCC 在启用
-D_GNU_SOURCE时会隐式暴露pthread_sigmask,无需额外头文件(只需<signal.h>)
常见错误:sigset_t 未初始化就传给 pthread_sigmask
典型错误是声明 sigset_t mask; 后直接传地址,没调用 sigemptyset 或 sigfillset —— 此时 mask 是未初始化栈变量,内容随机,pthread_sigmask 可能因读取非法位模式而失败(errno = EINVAL)或静默出错。
正确做法只有两种:
- 读取时:分配后直接传给
pthread_sigmask(..., nullptr, &mask)—— 此时函数负责填入有效值,无需预先初始化 - 写入时:必须先
sigemptyset(&mask)或sigfillset(&mask),再设置具体信号,最后传给pthread_sigmask
很多文档漏掉这点,导致新手反复踩坑:不是所有 sigset_t 操作都要求初始化,但“作为输出参数被写入”时,未初始化反而更安全;而“作为输入参数被读取”时,必须初始化。


















