当前位置: 首页 > news >正文

sigprocmask 函数深度解析

sigprocmask 函数深度解析

sigprocmask 是 Linux 信号处理的核心函数,用于控制进程的信号屏蔽字(Signal Mask)。它直接决定了哪些信号会被阻塞(blocked),是构建可靠信号处理机制的基础。


函数原型

#include <signal.h>int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);

参数详解

1. how:操作类型(行为模式)
宏定义值含义数学表达
SIG_BLOCK0set 中的信号添加到当前屏蔽字新掩码 = 当前掩码 ∪ set
SIG_UNBLOCK1set 中的信号移除出当前屏蔽字新掩码 = 当前掩码 - set
SIG_SETMASK2将当前屏蔽字直接替换set 指定的信号集新掩码 = set

⚠️ 非法值处理:如果 how 不是以上三个值,函数返回 -1 并设置 errno = EINVAL

2. set:输入信号集指针
含义
非NULL指向包含操作信号的 sigset_t 对象,具体操作由 how 决定
NULL不改变当前屏蔽字(此时 how 被忽略),仅用于获取当前屏蔽字到 oldset
3. oldset:输出信号集指针
含义
非NULL函数将在此存储修改前的信号屏蔽字
NULL不保存之前的屏蔽字

返回值

含义
0成功
-1失败,设置 errno

错误码(errno)

错误码含义常见触发场景
EFAULT无效的内存地址setoldset 指向非法地址
EINVAL无效的 how 参数how 不是 SIG_BLOCK/UNBLOCK/SETMASK

核心功能图解

SIG_BLOCK
SIG_UNBLOCK
SIG_SETMASK
调用 sigprocmask
how 类型?
新屏蔽字 = 当前屏蔽字 OR set
新屏蔽字 = 当前屏蔽字 AND NOT set
新屏蔽字 = set
oldset 非空?
保存旧屏蔽字到 oldset
完成

使用场景详解

场景1:阻塞特定信号(SIG_BLOCK)
sigset_t new_set;
sigemptyset(&new_set);
sigaddset(&new_set, SIGINT);  // 添加 SIGINT
sigaddset(&new_set, SIGTERM); // 添加 SIGTERM// 阻塞 SIGINT 和 SIGTERM
sigprocmask(SIG_BLOCK, &new_set, NULL);/* 临界区代码(不会被 SIGINT/SIGTERM 中断) */
场景2:解除信号阻塞(SIG_UNBLOCK)
sigset_t unblock_set;
sigemptyset(&unblock_set);
sigaddset(&unblock_set, SIGINT);// 解除 SIGINT 阻塞
sigprocmask(SIG_UNBLOCK, &unblock_set, NULL);
场景3:完全替换屏蔽字(SIG_SETMASK)
sigset_t full_set, old_set;
sigfillset(&full_set);  // 包含所有信号// 阻塞所有信号,保存旧屏蔽字
sigprocmask(SIG_SETMASK, &full_set, &old_set);/* 绝对安全临界区 */// 恢复原始屏蔽字
sigprocmask(SIG_SETMASK, &old_set, NULL);
场景4:获取当前屏蔽字(set=NULL)
sigset_t current_mask;// 获取但不修改当前屏蔽字
sigprocmask(SIG_BLOCK, NULL, &current_mask);// 检查 SIGINT 是否被阻塞
if (sigismember(&current_mask, SIGINT)) {printf("SIGINT is currently blocked\n");
}

特殊信号处理规则

  1. 不可阻塞信号

    // 尝试阻塞 SIGKILL/SIGSTOP 会被忽略
    sigset_t mask;
    sigaddset(&mask, SIGKILL);
    sigprocmask(SIG_BLOCK, &mask, NULL); // 实际无效
    
  2. 信号处理期间的自动屏蔽

    void handler(int sig) {// 执行期间自动阻塞当前信号 (SIGINT)// 同时阻塞 sa_mask 中指定的信号
    }int main() {struct sigaction sa;sa.sa_handler = handler;sigemptyset(&sa.sa_mask);sigaddset(&sa.sa_mask, SIGQUIT); // 额外屏蔽 SIGQUITsigaction(SIGINT, &sa, NULL);
    }
    

多线程注意事项

// 错误!进程级函数影响所有线程
sigprocmask(SIG_BLOCK, &set, NULL); // 正确:线程安全版本
pthread_sigmask(SIG_BLOCK, &set, NULL);
特性sigprocmaskpthread_sigmask
作用范围整个进程单个线程
线程安全
标准POSIX 进程标准POSIX 线程标准
推荐场景单线程程序/主线程初始化多线程程序的信号控制

原子操作保障

危险的非原子操作:
// 第1步:解除阻塞
sigprocmask(SIG_UNBLOCK, &set, NULL);// 此处可能发生信号递送并丢失!// 第2步:等待信号
pause(); // 可能永远阻塞
安全的原子方案:
// 准备等待的信号集
sigset_t wait_mask;
sigemptyset(&wait_mask);
sigaddset(&wait_mask, SIGINT);// 原子操作:设置临时屏蔽+等待
sigsuspend(&wait_mask); // 安全等待SIGINT

完整示例:临界区保护

#include <signal.h>
#include <stdio.h>
#include <unistd.h>void critical_section() {sigset_t new_mask, old_mask;// 步骤1:准备阻塞所有信号sigfillset(&new_mask);// 步骤2:设置屏蔽字(进入临界区)if (sigprocmask(SIG_SETMASK, &new_mask, &old_mask) == -1) {perror("sigprocmask failed");return;}/* ===== 临界区开始 ===== */printf("Entered critical section\n");// 模拟关键操作for (int i = 0; i < 3; i++) {write(STDOUT_FILENO, ".", 1);sleep(1);}write(STDOUT_FILENO, "\n", 1);/* ===== 临界区结束 ===== */// 步骤3:恢复原始屏蔽字if (sigprocmask(SIG_SETMASK, &old_mask, NULL) == -1) {perror("sigprocmask restore failed");}
}int main() {// 设置SIGINT处理signal(SIGINT, SIG_IGN); // 简单忽略printf("Try Ctrl+C during critical section:\n");critical_section();printf("Now Ctrl+C will work:\n");pause(); // 等待信号return 0;
}

运行效果

Try Ctrl+C during critical section:
Entered critical section
...(此时按Ctrl+C无效)
...
Now Ctrl+C will work:
^C  # 程序退出

最佳实践总结

  1. 初始化信号集

    sigset_t set;
    sigemptyset(&set); // 或 sigfillset(&set)
    
  2. 错误检查

    if (sigprocmask(SIG_BLOCK, &set, NULL) == -1) {perror("sigprocmask error");// 错误处理
    }
    
  3. 资源清理

    sigset_t orig_mask;
    sigprocmask(SIG_SETMASK, &new_mask, &orig_mask);
    /* 操作 */
    sigprocmask(SIG_SETMASK, &orig_mask, NULL); // 恢复
    
  4. 配合 sigsuspend

    sigset_t wait_mask;
    // 配置 wait_mask...
    sigsuspend(&wait_mask); // 原子等待
    
  5. 线程环境

    // 使用 pthread_sigmask
    pthread_sigmask(SIG_BLOCK, &set, NULL);
    

sigprocmask 是信号控制的核心枢纽,理解其参数机制和与其它信号函数的协作关系,是编写健壮 Linux 系统程序的关键基础。

http://www.lryc.cn/news/613605.html

相关文章:

  • 【指南版】网络与信息安全岗位系列(三):安全运维工程师
  • Redis 分布式Session
  • Redis面试精讲 Day 16:Redis性能监控与分析工具
  • 锡膏种类多,不同的锡膏有什么区别,该如何正确选择?
  • 深入理解 ReentrantLock和AQS底层源码
  • Day09 Tlisa登录认证
  • 计算机英语详细总结
  • 类和对象(中):类的默认成员函数、构造函数、析构函数
  • MinHash算法:为什么选择Min而不是Max
  • DM数据库集群操作顺序规范
  • Linux线程学习
  • 分布式面经
  • Redis面试精讲 Day 14:Redis分片策略与一致性Hash
  • Debain12 api方式部署redis服务
  • 51c大模型~合集165
  • Tiger任务管理系统-10
  • Java 中 Object 类的解析:知识点与注意事项
  • 每日五个pyecharts可视化图表-bars(6)
  • [激光原理与应用-170]:测量仪器 - 能量型 - 光功率计的工作原理与内部功能模块组成
  • 语言模型(LM):n-gram模型原理与困惑度(Perplexity)计算详解
  • 快速搭建vue3+flask实现一个异物检测项目
  • 深入理解“进程屏蔽字“(Signal Mask)
  • Qt——入门
  • STM32学习笔记4-OLED外部中断和中断系统
  • 【C#补全计划:类和对象(九)】接口
  • 【Agent】ReAct:最经典的Agent设计框架
  • RP2040下的I2S Slave Out,PIO状态机(三)
  • 解决winform中的listbox实现拖拽时,遇到combox控件会闪烁的问题
  • 数据库事务总结
  • 嵌入式开发硬件——单片机