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

【Linux笔记】——进程信号的保存

🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:Linux
🌹往期回顾🌹:【Linux笔记】——进程信号的产生
🔖流水不争,争的是滔滔不


  • 一、信号的相关概念
  • 二、信号集操作函数
    • sigset_t
    • 信号集操作函数
    • sigprocmask
    • sigpending
  • term 和 core

一、信号的相关概念

  1. 实际执行信号的处理动作称为信号递达(Delivery)。
  2. 信号从产生到递达之间的状态,称为信号未决(Pending)。
  3. 进程可以选择**阻塞(Block)**某个信号。
  4. 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才能执行递达的动作。
  5. 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

这些概念都是内核完成的
在这里插入图片描述
如图,block就是阻塞,pending就是未决,handler是信号处理函数就是信号来了以后“干具体事情的地方”,它才是最终决定信号怎么被响应的“执行动作”。

对block和pending进行一个比喻,block可以理解为拦着不让外卖员敲门(不想收外卖时挂个禁止通行的牌子),而pending可以理解为外卖员到了,但是被门外拦着(外卖员在门口堵着)。

简单梳理一下handler触发的整个流程:

  1. 信号来了(内核识别到,不管是硬件还是软件发来的信号)
  2. 判断block:block是一个信号信号阻塞位图,每一位对应一个信号标号1表示阻塞(block了,不让送达)0表示不阻塞(可以送达)。
  3. 检测block位图中,信号对应的那一位,如果是1,则信号是挂到pending位图中,若是0,则理解开始信号处理流程,调用用户注册的handler函数。

注意:
信号来了。不管block了没有,这个信号都会被挂到pending位图中(置为1),也就是说信号来了就记下来了pending代表“来了”。
然后才会去看block位图,如果这个信号在block中被阻塞,信号就会一直挂在pending中,等待解封。如果block没有阻塞,就交个handler执行啦。

还是看上图,pending位图保存收到的信号位图,比特位的位置:表示的是第几个信号,比特位的内容:表示是否收到。block位图表示是否阻塞,比特位的位置:表示的是第几个信号,比特位的内容表示:是否阻塞。

二、信号集操作函数

sigset_t

从上图来看,每个信号只有一个bit的未决标志, 非0即1, 不记录该信号产生了多少次,阻塞标志也是这样
表示的。因此, 未决和阻塞标志可以勇相同的数据类型sigset_t来存储, , 这个类型可以表示每个信号的“有效”或“无效”状态, 在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞, 而在未决信号集中“有 效”和“无效”的含义是该信号是否处于未决状态。阻塞信号集也叫做当前进程的 这里的“屏蔽”应该理解为阻塞⽽不是忽略。

信号集操作函数

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(const sigset_t *set, int signo)

操作信号集(sigset_t) 的一堆函数,干的都是“组装、修改、判断信号集”的活。
这些信号集其实是用户态的临时数据结构,本质上是位图,跟内核里block位图的存储形式类似。

典型函数:
sigemptyset(清空信号集,全是0)
sigfillset(全填1)
sigaddset(添加某个信号)
sigdelset(删除某个信号)
sigismember(判断某信号在不在信号集中)

这四个函数都是成功返回0,出错返回-1。sigismember是⼀个布尔函数,⽤于判断⼀个信号集的有效信
号中是否包含 某种 信号,若包含则返回1,不包含则返回0,出错返回-1。

sigprocmask

调用函数 sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)。 这个系统调用才是真正去修改内核中当前进程的 block 位图。
它会用你准备好的信号集(sigset_t)去 “替换、添加、删除” block 位图中对应的位。所有这里要注意上面的信号集操作函数只是工具,你拿着信号集草稿去找 sigprocmask 办手续,sigprocmask 帮你真正去 kernel 里改。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
返回值:若成功则为0,若出错则为-1

参数有:
how:表示是“添加(阻塞)”、“删除(解除阻塞)”、“直接替换成新的”。
set:你准备好的信号集。
oldset:可以把修改前的block值保存下来。

在这里插入图片描述
这些都是修改block位图。

sigpending

#include <signal.h>
int sigpending(sigset_t *set);
读取当前进程的未决信号集,通过set参数传出。
调⽤成功则返回0,出错则返回-1

不管你爱不爱搭理(阻塞不阻塞),信号一到,pending 位图对应的位置就会被置 1。
不会因为 block、不会因为 handler 正忙,就不记。→ 来一个挂一个, pending 只负责“有信号来了”的事实,不负责“要不要执行”。

那sigpending() 是干嘛的?

查询“当前进程”哪些信号已经挂在 pending 位图上(那些“已经到了但是还没被响应”的信号)。
用于排查信号是否被 block 阻塞住了。
诊断信号处理的状态,是个“监测手段”,不是“处理动作”。

实验

#include <iostream>
#include <unistd.h>
#include <cstdio>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>// 打印 pending 集
void PrintPending(sigset_t &pending) {std::cout << "curr process[" << getpid() << "] pending: ";for (int signo = 31; signo >= 1; signo--) {if (sigismember(&pending, signo)) {std::cout << 1;} else {std::cout << 0;}}std::cout << "\n";
}// 信号处理函数
void handler(int signo) {std::cout << signo << " 号信号被递达!!!" << std::endl;std::cout << "-------------------------------" << std::endl;sigset_t pending;sigpending(&pending);PrintPending(pending);std::cout << "-------------------------------" << std::endl;
}int main() {// 0. 捕捉 2 号信号signal(SIGINT, handler);  // 自定义捕捉 SIGINT (Ctrl+C)// 1. 屏蔽 2 号信号sigset_t block_set, old_set;sigemptyset(&block_set);sigemptyset(&old_set);sigaddset(&block_set, SIGINT);// 1.1 设置进程的 Block 表sigprocmask(SIG_BLOCK, &block_set, &old_set);int cnt = 15;while (true) {// 2. 获取当前进程的 pending 信号集sigset_t pending;sigpending(&pending);// 3. 打印 pending 信号集PrintPending(pending);cnt--;// 4. 解除对 2 号信号的屏蔽if (cnt == 0) {std::cout << "解除对 2 号信号的屏蔽!!!" << std::endl;sigprocmask(SIG_SETMASK, &old_set, &block_set);}sleep(1);}return 0;
}

前面 15 秒,Ctrl+C 打了很多次,都不会触发 handler,因为 SIGINT 被 block 了,但 pending 位图记录了。

你看到的:

curr process[448336] pending: 0000000000000000000000000000010

代表第 2 号信号 (SIGINT) 被挂在 pending 里。

cnt==0 时解除阻塞,pending 里的 SIGINT 会被马上递达,调用 handler,打印信号收到。

term 和 core

子进程退出时产生的 core 和 term 现象,本质上也是由信号触发的结果,不过它们不单纯是“信号”本身,而是信号带来的退出状态。

core会在当前路径下,形成一个文件,进程异常退出的时候,进程在内存中的核心数据,会从内存拷贝到磁盘,形成一个文件的核心转储,并且支持debug。然后在进程退出
trem就直接进程退出了。

子进程的 term / core 是“信号导致的退出状态”,本质是信号事件的结果表现。term 代表被终止,core 代表还顺便扔了个尸体(core dump 文件)出来。

http://www.xdnf.cn/news/5551.html

相关文章:

  • 51单片机引脚功能概述
  • 十五、多态与虚函数
  • labview硬件采集
  • 数字人教学技术与产品方案的全面解析
  • 42、在.NET 中能够将⾮静态的⽅法覆写成静态⽅法吗?
  • 本地不安装oracle,还想连oracle
  • c++STL-STL简介和vector的使用
  • ngx_http_keyval_module动态键值管理
  • 基于STM32、HAL库的RN8209C电能计量芯片驱动程序设计
  • 系统架构-嵌入式系统架构
  • AI 搜索引擎 MindSearch
  • 香港维尔利健康科技集团亮相中国资本市场发展年会,被评为“最具投资价值医疗科技企业”
  • 面试题解析 | C++空类的默认成员函数(附生成条件与底层原理)
  • 高吞吐与低延迟的博弈:Kafka与RabbitMQ数据管道实战指南
  • 互联网大厂Java求职面试:优惠券服务架构设计与AI增强实践-1
  • 七、基于HAL库,实现串口+DMA+状态机通信实现
  • 国产化Excel处理控件Spire.XLS系列教程:如何通过 C# 删除 Excel 工作表中的筛选器
  • HTML简单语法标签(后续实操:云备份项目)
  • 《Spring Boot 4.0新特性深度解析》
  • 企业即时通讯软件,私有化安全防泄密
  • 图灵爬虫练习平台第十九题js逆向
  • 使用基于ARM的低功耗微型单板计算机打造智能家居管理系统中枢
  • 开发环境(Development Environment)
  • 前端面试每日三题 - Day 32
  • Kubernetes控制平面组件:Kubelet详解(二):核心功能层
  • Go语言:json 作用和语法
  • 【WPF】Opacity 属性的使用
  • 【Redis 进阶】哨兵模式
  • 降低60.6%碰撞率!复旦大学地平线CorDriver:首次引入「走廊」增强端到端自动驾驶安全性
  • 芯显10.4寸工业液晶屏XB104S01-200-10.4寸工业显示屏