信号保存:Linux进程信号保存 + 进程信号阻塞设置 + 查看接收到的所有信号

信号的发送方式有四种,在被发送后,我们知道如果暂时不处理信号,信号会被保存在进程控制块中,而且是以位图的方式保存;等时机合适了,就去处理收到的信号

下面会从内核的角度作进一步的分析,信号被发送后,可以被进程阻塞!!不仅如此,我们还能查看进程收到了哪些信号


目录

一、信号从发送到被处理经历的过程(内核角度)

1、信号常见概念

(1) 信号阻塞

(2) 信号未决

(3) 信号递达

(4) 信号忽略(与信号阻塞的区别)

2、信号保存的方式(内核级)

3、信号从发送到被处理

4、总结

二、信号集操作(信号保存)

1、特定的数据类型sigset_t

2、操作信号集的函数

(1) sigemptyset函数:清空信号集

(2) sigfillset函数:初始化信号集

(3) sigaddset函数:向信号集中添加一个信号

(4) sigdelset函数:从信号集中删除一个信号

(5) sigismember函数:判断某个信号是否为信号集的成员(判断是否在信号集中)

三、信号阻塞/解除阻塞函数 sigprocmask(修改block表)

1、参数解析

(1) 第一个参数 how 

(2) 第二个参数 set

(3) 第三个参数 oset

2、小测试:屏蔽/阻塞 2号信号和8号信号

四、查看当前进程收到的所有信号 sigpending函数(获取pending表)


一、信号从发送到被处理经历的过程(内核角度)

1、信号常见概念

(1) 信号阻塞

信号被发送后,分为两种情况,一种是被阻塞了(被拉黑了),一种是没有被阻塞

举一个例子,一个粉丝想给xx明星送礼物,礼物由经纪人代为接收。

——》如果被阻塞了,虽然礼物确实是收到了,但是送的礼物在黑名单里,经纪人不会递达给xx明星,所以最后该明星看不到这个礼物;

——》如果没有被阻塞,而且经纪人收到礼物了,这个时候xx明星就会去处理这件礼物。

(2) 信号未决

在信号被进程处理之前的过程,都可以称为信号未决

(3) 信号递达

信号被进程处理,我们称为信号递达

(4) 信号忽略(与信号阻塞的区别)

继续上面的例子,xx明星处理礼物的时候,可以选择忽略这件礼物,也可以在收到礼物以后给粉丝回信,所以信号忽略是进程处理信号的一种方式

而信号阻塞呢?进程压根看不到这个信号!!

2、信号保存的方式(内核级)

下面我们通过一张图来理解,信号在进程控制块的存储方式是以位图的方式

block表:对应的信号是否被阻塞,0表示不阻塞,1表示阻塞

pending表信号是否被收到,0表示未收到该信号,1表示收到该信号

handler表:表示对应信号的处理方式(函数),存放的是函数的地址(函数指针)

3、信号从发送到被处理

上面这个图我们要看一行

红色字代表第 N 号信号,以第二行为例,block 为 1 ,说明该信号被阻塞了(相当于在进程的黑名单里),后面的pending是0还是1都无所谓了

以第三行为例,block为0,该信号没有被阻塞,pending为1,说明收到这个信号了,对应的处理方式是 sighandler函数,那就会执行该函数

注意:

SIG_DFL(signal default):信号默认处理方式

SIG_IGN(signal ignore):忽略信号

4、总结

先看block,如果block为1,即信号被拉黑,是否收到信号都不重要了

如果block不为1,那就再看pending是否为1,即是否收到信号

二、信号集操作(信号保存)

阻塞信号其实就是要修改block表,但是要怎么修改呢?如果只是传递单个信号,或许可以直接以某种方式告诉OS我希望阻塞哪个信号,但是,如果要修改多个信号呢??OS给出了一种方案来解决传递单个或者多个信号,那就是直接传递位图!!

1、特定的数据类型sigset_t

既然要传递位图,要用什么来表示位图呢?第一想法是int/uint32_t,不同的操作系统,实现位图的方式可能有所不同,Linux操作系统不光给我们提供了 保存位图的数据类型 sigset_t,还有对应的函数来操作位图(下面说)

——》sigset_t 可以看作是一个信号集,保存着1~31号信号的状态,0表示没收到,1表示收到了

注意:这个位图和我们以往遇到的位图不一样,并不适用 &、|、^等操作

2、操作信号集的函数

操作信号最基本的操作无非就是增删查改,下面就逐一来介绍

(1) sigemptyset函数:清空信号集

sigemptyset函数将位图所有的比特位置0。按照以往的习惯,清空不就是与上一个 0 吗?但是前面也说了,不适用!下面是这个函数的声明

 参数是传入一个信号集的地址,成功返回0,失败返回-1。下面是使用方法

(2) sigfillset函数:初始化信号集

sigfillset函数将位图所有的比特位都置为1

 参数是传入一个信号集的地址,成功返回0,失败返回-1。

(3) sigaddset函数:向信号集中添加一个信号

假设一开始所有的比特位都是 0(0000 0000 ...),如果我们希望把第二个信号阻塞,那么就需要先把信号集的第二个比特位设置为1(0100 0000....),然后再传给信号阻塞函数

 第一个参数是传入信号集的地址,第二个参数是要把哪个位置(信号)设置成1,返回值同上

(4) sigdelset函数:从信号集中删除一个信号

(假设前面已经阻塞了2号信号)这个时候的比特位显示为0100 0000....,现在我们不希望阻塞2号信号,那么需要把第二个位置(信号)设置为 0 (0000 0000....)

 参数解析同sigaddset函数

(5) sigismember函数:判断某个信号是否为信号集的成员(判断是否在信号集中)

如果对应位置(信号)的比特位为1,说明在信号集中,如果为0,说明不在信号集中

 成功返回1,失败返回0

三、信号阻塞/解除阻塞函数 sigprocmask(修改block表)

信号阻塞也可以称为信号屏蔽,这里的mask是屏蔽的意思。下面是这个函数的声明,头文件依然是signal.h

1、参数解析

(1) 第一个参数 how 

信号的阻塞与否分为三种情况,也就对应了三个可选值

第一种,在原有的基础上,添加要阻塞的信号                 —— SIG_BLOCK

第二种,在原有的基础上,解除一些信号的阻塞限制      —— SIG_UNBLOCK

第三种,按照自己传入的信号集来设置要阻塞的信号      —— SIG_SETMASK

(2) 第二个参数 set

这是一个输入型参数,输入你希望屏蔽或者阻塞的一个/多个信号

(3) 第三个参数 oset

这是一个输出型参数,输出在你修改block之前的block表(或者叫做信号屏蔽字)

2、小测试:屏蔽/阻塞 2号信号和8号信号

测试内容是这样,一开始,屏蔽2号和8号信号,5s后解除屏蔽,如果收到信号就打印收到了几号信号。具体代码如下(Makefile省略不展示)

 这里我们测试两次,第一次测试,前5s内按下Ctrl + C,看一下是否会中止进程;第二次测试,5s后按下Ctrl + C,看一下解除以后是否会中止

第一次测试我们发现,按下Ctrl + C以后,进程继续运行,等到限制解除以后,看到自己收到了2号信号,然后就去处理2号信号 

四、查看当前进程收到的所有信号 sigpending函数(获取pending表)

=======================sigpending函数=======================

不对pending位图进行修改,只是获取pending表里的内容。你或许会觉得奇怪,但不要忘记,进程会自己捕获信号,捕获到的信号由OS将pending表的对应位置由 0 改为 1 。获取pending表的函数声明如下

 参数是一个输出型参数,我们可以通过sigismember函数查看是否收到了某个信号 。成功时返回0,失败返回-1

=======================结合信号阻塞案例测试=======================

现在我们可以把上面的printf("process is running") 换成打印pending表中的31个信号的位图

pending表信号集我们可以通过sigpending获取,但是要如何打印呢??

我们可以通过sigismember来看看每一个信号的接收状态,如果接收到了,打印1,否则打印0

 从测试结果我们发现,即便信号被阻塞,pending表也会显示信号接收到了,一旦阻塞解除,就会展示给进程,让进程处理这个信号

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值