当前位置: 首页 > 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.dtcms.com/a/318806.html

相关文章:

  • html页面使用jspdf预览及下载pdf模板
  • 使用驱动移除内核回调,
  • golang开源库之Syncthing
  • Unity URP渲染管线动态修改材质球状态
  • 基于 HT 引擎实现 3D 智慧物流转运中心一体化管控系统
  • 【CS创世SD NAND征文】小型夜灯为何需要存储芯片?从基础照明到智能存储的升级密码
  • 生成式AI时代,Data+AI下一代数智平台建设指南
  • EP04:【DL 第二弹】张量的线性代数运算
  • 内网穿透原理和部署教程
  • 京东关键字搜索商品列表接口开发实战:从参数优化到分布式调用
  • localforage的数据仓库、实例、storeName和name的概念和区别
  • VBA之Word应用第四章第一节:段落集合Paragraphs对象(一)
  • mysql全屏终端全量、部分备份、恢复脚本
  • 累加和校验原理与FPGA实现
  • 躺平发育小游戏微信抖音流量主小程序开源
  • 自建纯竞拍系统小程序需准备的事项
  • uniapp/uniappx实现图片或视频文件选择时同步告知权限申请目的解决华为等应用市场上架审核问题
  • TSMaster-C小程序使用
  • uni-app X能成为下一个Flutter吗?
  • Dify 从入门到精通(第 20/100 篇):Dify 的自动化测试与 CI/CD
  • MyBatis-Plus Service 接口:如何在 MyBatis-Plus 中实现业务逻辑层??
  • 阿里云部署若依后,浏览器能正常访问,但是apifox和小程序访问后报错链接被重置
  • [失败记录] 使用HBuilderX创建的uniapp vue3项目添加tailwindcss3的完整过程
  • [无需 Mac] 使用 GitHub Actions 构建 iOS 应用
  • vue3 el-select 加载内容后 触发事件
  • 「耘•学社」耘少年第五期学能突破导师制领袖特训营,圆满落幕
  • C++与SparkAI实战:高效应用案例
  • Android-Kotlin基础(Jetpack②-Data Binding)
  • 国产化Excel处理组件Spire.XLS教程:使用 C# 将 DataTable 导出为 Excel 文件
  • 嵌入式C语言编程:策略模式、状态模式和状态机的应用