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

Linux进程信号(贰):保存信号

1.信号相关常见概念

什么是信号保存?

信号保存是指内核在信号产生递送之间,对信号状态和信息进行维护的过程。当信号无法立即被进程处理时,内核需要暂时保存这些信号。

信号的生命周期:

信号产生信号保存信号递送(处理)

信号的三个状态

产生(Generation):信号被某个事件或进程创建

未决(Pending):信号已产生但尚未递送给目标进程

递送(Delivery):信号被目标进程接收并处理

• 实际执行信号的处理动作称为信号递达(Delivery)

• 信号从产生到递达之间的状态,称为信号未决(Pending)。

• 进程可以选择阻塞 (Block )某个信号。

被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作.

• 注意,阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

2.在内核中的表示

信号在内核中的表示示意图

进程可以识别信号,本质靠的是这三张表,进程对信号的操作实际上都是对这三张表的操作

block表和pending表本质是结构体中包含数组表示位图,数组下标表示信号编号,下标中的内容1/0表示是否

• 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。

• SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。

• SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。本章不讨论实时信号。

3.关键数据结构

sigset_t - 信号集

#define _NSIG 64
#define _NSIG_BPW 64
#define _NSIG_WORDS (_NSIG / _NSIG_BPW)typedef struct {unsigned long sig[_NSIG_WORDS];
} sigset_t;

使用位图表示信号集合

每个bit代表一个信号(1-64)

用于阻塞掩码、未决信号集等

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

struct sigpending - 待处理信号

struct sigpending {struct list_head list;              // 信号队列链表头sigset_t signal;                    // 未决信号位图
};

4.信号集操作函数

这些函数都是同一个头文件

1. 基本信号集操作函数

sigemptyset

#include <signal.h>int sigemptyset(sigset_t *set);
  • 功能:初始化信号集,清空所有信号
  • 参数:set - 指向信号集的指针
  • 返回值:成功返回0,失败返回-1

sigfillset

int sigfillset(sigset_t *set);
  • 功能:初始化信号集,包含所有信号
  • 参数:set - 指向信号集的指针
  • 返回值:成功返回0,失败返回-1

sigaddset

int sigaddset(sigset_t *set, int signum);
  • 功能:向信号集中添加指定信号
  • 参数:
    • set - 信号集指针
    • signum - 要添加的信号编号
  • 返回值:成功返回0,失败返回-1

sigdelset

int sigdelset(sigset_t *set, int signum);
  • 功能:从信号集中删除指定信号
  • 参数:
    • set - 信号集指针
    • signum - 要删除的信号编号
  • 返回值:成功返回0,失败返回-1

sigismember

int sigismember(const sigset_t *set, int signum);
  • 功能:检查信号是否在信号集中
  • 参数:
    • set - 信号集指针
    • signum - 要检查的信号编号
  • 返回值:在集合中返回1,不在返回0,错误返回-1

2. 进程信号掩码操作函数

int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
  • 功能:调用函数sigprocmask 可以读取或更改进程的信号屏蔽字(阻塞信号集)。
  • 参数:
    • how - 操作方式:假设当前的信号屏蔽字为mask,下表说明了how参数的可选值。

- <font style="color:rgb(15, 17, 21);">set - 新的信号集</font>
- <font style="color:rgb(15, 17, 21);">oset - 保存原来的信号集</font>
  • 返回值:成功返回0,失败返回-1

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。如果set是非空指针,则 更改进程的信 号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后 根据set和how参数更改信号屏蔽字。

如果调用sigprocmask解除了对当前若干个未决信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。

3. 等待信号函数

int sigpending(sigset_t *set);
  • 功能:读取当前进程的未决信号集,通过set参数传出。
  • 参数:set - 用于返回未决信号的信号集
  • 返回值:成功返回0,失败返回-1

5.代码实践

test:test.cppg++ -o $@ $^ -std=c++11
.PHONY:clean
clean:rm -f test
#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;void PrintPending(sigset_t &pending)
{// 左->右=高位->低位,从左向右高位到低位打印0000……0000cout << "当前进程pending信号集:";for (int i = 31; i > 0; i--){// 检测i号信号是否在信号集中,如果在,打印1,否则,打印0if (sigismember(&pending, i)){cout << "1";}elsecout << "0";}cout << endl;
}
void handler(int signal)
{// 防止解除屏蔽后信号递达进程直接退出看不到现象// 验证pending信号集是在递达前变化的// 定义pending信号集sigset_t pending;sigemptyset(&pending);// 获取当前进程的pending信号集,实际上就是将内核中的pending表拷贝到栈上的pending信号集中sigpending(&pending);// 打印pending信号集cout<<"##################################"<<endl;PrintPending(pending);cout<<"##################################"<<endl;//如果pending信号集在handler函数结束cout << "信号递达了" << endl;
}
int main()
{signal(2, handler);// 设置信号屏蔽字block,oblock为原来的信号屏蔽字// 当前设置的信号集都是用户层面的,在栈上定义的,要想设置内核层面的,需系统调用sigset_t block, oblock;// 变量定义时为随机值,为确保信号集准确性需将其清空sigemptyset(&block);sigemptyset(&oblock);// 将2号信号添加到block中sigaddset(&block, 2);// 设置内核的信号屏蔽字sigprocmask(SIG_SETMASK, &block, &oblock);int cnt = 5;while (true){// 定义pending信号集sigset_t pending;sigemptyset(&pending);// 获取当前进程的pending信号集,实际上就是将内核中的pending表拷贝到栈上的pending信号集中sigpending(&pending);// 每隔一秒打印一次pending信号集PrintPending(pending);sleep(1);cnt--;// 5秒后解除屏蔽if (cnt == 0){cout << "当前进程解除屏蔽了" << endl;sigprocmask(SIG_SETMASK, &oblock, nullptr);}}return 0;
}

补充知识:

1.和信号捕捉一样,进程并不能屏蔽所有信号,9号信号和19号信号不可被屏蔽

2.在解除屏蔽后,pending信号集是在信号递达前变化还是在在信号递达后变化?

答案是在信号递达前变化

信号处理的时间线:

1. 信号产生 → 2. 信号加入 pending 集 → 3. 解除屏蔽 → 4. 从 pending 集移除 → 5. 信号递达

我们想一下就可以理解,大部分信号的默认处理都是终止进程,信号递达后,进程已经终止了,还这么改变pending信号集

http://www.dtcms.com/a/520689.html

相关文章:

  • 互联网站建设 天津台州网站建设惠店
  • 基于Python大数据的主流汽车价格分析可视化系统
  • Flutter状态管理原理详解
  • 如何选择网站项目企业营销推广怎么做
  • MCP Server 启动和应用
  • C语言通过函数实现素数验证
  • 软件无线电关键技术--基带QPSK 调制技术
  • Linux网络——应用层序列化反序列化
  • EWCCTF2025 Tacticool Bin wp
  • 【Trae+AI】和Trae学习搭建App_01(附加可略过):测试Trae的后端功能
  • 网站源码 下载查域名价格
  • 上海做网站联系电话山东兴华建设集团有限公司网站
  • 使用 Vue3 和 Element Plus 实现选择新增用户集下拉选项框,切换类型,有物业,网格,电子围栏,行政区划管理
  • Vue项目页面间,页面中跳转及刷新规划,何时使用router-view,router-link,iframe,slots ,使用场景,及对应场景的完整使用示例
  • 【Qt】VS Code配置Qt UI插件,vscode打开Qt Designer ,vscode打开Qt*.ui文件
  • 服务网站建设的公司安装网站系统
  • 直播做网站数字广东网络建设有限公司介绍
  • 宇树科技:决定更名
  • 2025年MathorCup 大数据竞赛明日开赛,注意事项!论文提交规范、模板、承诺书正确使用!2025年第六届MathorCup数学应用挑战赛——大数据竞赛
  • 【案例实战】鸿蒙智能日程应用性能优化实战:从卡顿到丝滑的完整历程
  • 创建网站商城电子商务企业网站建设前期规划方案
  • php租车网站源码营销型网站规划
  • Universal Extractors (万能解压器) 支持500+格式
  • 网站策划岗位要求wordpress htaccess文件
  • Google Play多区测试与真机复现:用纯净IP重现真实流量(含技术方案)
  • Lombok是什么?
  • 淘客网站做单品类wordpress词汇插件
  • 内网穿透的应用-从崩溃到流畅!Web-Check+cpolar的站点优化实战
  • opencv模版匹配
  • Cython 出现‘Failed to Map Segment from Shared Object‘错误