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

【Linux】信号保存

目录

一、信号的保存:

二、内核中的信号表示:

handler:

pending:

block:

sigset_t:

sigprocmask:

sigpending:

应用:


一、信号的保存:

当我们信号产生后,并不会立即处理这个信号,而是有一段时间窗口,在这个时间窗口内,信号没有被处理,而是保存起来了,那么进程是怎样保存对应的信号的呢?

我们知道,一定是OS给进程发送信号的,并且这个信号肯定是在我们进程的PCB中,所以在进程的PCB中就一定有一个变量来存放信号,这个变量就是一个整数int,当然,我们不能够仅仅看到这个整数,更要看到的是这是一个位图结构:

1、比特位的内容是1就证明着当前进程接受到了信号
2、第几个比特位被修改位1,就证明收到了几号信号

3、那么OS向进程发送信号的本质就是OS修改PCB中,对应信号的位图结构的对应比特位,所以OS发信号就是首先找到进程里面描述信号的字段,然后将对应的比特位的位置修改为1

当OS向我们的进程发送多个相同信号的时候,并不会处理很多次,只会执行一次(这就相当于当我们在打游戏的时候,同时妈妈喊我们吃饭,会喊很多次,但是我们仅仅只会最后执行一次),毕竟位图只有0和1的结构,但是以上是对于普通信号的,如果是实时信号,那么发送了几次就要执行几次,是采用的是队列数据结构进行管理的

接下来了解关于信号的新概念:

信号产生:由上一篇文章所讲的几种产生方式

信号递达:实际执行信号的处理动作

信号未决:信号从产生到递达之间的状态

信号阻塞:也可叫做信号屏蔽,可随时屏蔽阻塞,并且在解除屏蔽之前都无法进行信号处理

当信号递达了就会有3种处理方式:
默认处理(SIG_DFL),忽略(SIG_IGN),自定义动作(handler)

二、内核中的信号表示:

如上,每一个进程都维护着如上的三张表,分别是block,pending和handler,其中block和pending表都是位图结构的,handler就是处理方式,这三张表是要横着看的

handler:

handler表就是当信号递达1时的处理方式,这是一个有着31个成员的函数指针数组(我们只考虑普通信号,所以也就是只有31个位置)格式为:返回值为空,参数为int的函数

如下是源码:

/* Type of a signal handler.  */
typedef void (*__sighandler_t) (int);

/* Fake signal functions.  */
#define SIG_ERR	((__sighandler_t) -1)		/* Error return.  */
#define SIG_DFL	((__sighandler_t) 0)		/* Default action.  */
#define SIG_IGN	((__sighandler_t) 1)		/* Ignore signal.  */

如上,这个handler表的自定义中就是使用signal()函数重新设定对应的动作

pending:

当pending中的对应信号的值为1就证明这个信号是处于未决状态,所以如何记录已经产生的信号? ----- 将pending表中的对应的比特位置为1即可

所以pending表中存放的就是该信号产生到未处理的过程中的信号,这就是未决状态,

其实,我们信号的实现,是模拟的我们的硬件中断,这里的pinding信号的编号,就类似于中断编号,而函数指针数组,就类似于我们的中断向量表

block:

这个就是将对应的信号进行屏蔽了,这样,即使收到了该信号,也不会进行递达,除非对这个信号解除屏蔽,这里的block数组和pending数组类似,都是位图结构,

关于屏蔽的理解:屏蔽只是一种状态,和信号有没有产生没有任何关系,只是如果将信号屏蔽了,然后信号产生后就会存在未决,而如果信号没有屏蔽,而信号产生后就会到信号递达

信号忽略与信号阻塞:

这两者是不同的概念的,信号忽略是已经信号递达了,然后对这个信号的处理动作是忽略的

信号阻塞是信号屏蔽,当产生信号的时候对其屏蔽,使其一直处于信号未决状态,没有对其进行处理

注意:

和捕捉信号一样,9号信号和19号信号不能被屏蔽

sigset_t:

上述三张表是OS内核中的数据,所以在用户层是不能够直接对其进行修改的,所以OS就给我们用户层提供了接口,用来对信号进行处理的,当在用户层和内核层进行数据的交互的时候,就需要进行数据的拷贝,所以OS就需要设计输入输出型参数来进行处理,所以就有了位图结构:四个sigset_t 这个数据类型,这个数据类型底层上就是封装了一个unsigned long int 类型的数组,如下

我们是不能够直接对这个类型进行位操作的,需要使用OS提供的系统调用接口来进行操作

#inlcude <signal.h>库文件 

int sigemptyset(sigset_t *set);清空信号集

int sigfillset(sigset_t *set);设置位图,将位图全部置为1

int sigaddset (sigset_t *set, int signo);向指定的信号集中添加特定的信号

int sigdelset(sigset_t *set, int signo);在指定的信号集当中,去掉一个信号

int sigismember(const sigset_t *set, int signo);判断一个信号是否在信号集当中

sigprocmask:

调用sigprocmask可以读取或者更改进程的的信号屏蔽字(阻塞信号集)

形参how有三个选项:

SIG_BLOCK希望添加至当前进程block表中阻塞信号,从set信号集中获取,相当于 mask |= set
SIG_UNBLOCK解除阻塞状态,也是从set信号集中获取,相当于 mask &= (~set)
SIG_SETMASK设置当前进程的block表为set信号集中的block表,相当于mask=set

如果oldset是非空指针,则读取进程的当前信号屏蔽字通过oldset参数传出。如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改

如果oldset和set都是非空指针,则先将原来的信号屏蔽字备份到oldset里,然后根据set和how参数更改信号屏蔽字

如上,返回值:成功返回0,失败返回-1,设置错误码

sigpending:

这个函数就是获取当前进程的信号集,将其从内核中带到用户层

参数:将当前进程的未决信号集带出来

返回值:成功零被返回,失败-1被返回,错误码被设置

应用:

void Printpending(sigset_t pending)
{
    for(int i = 31; i>=1; i--)
    {
        if(sigismember(&pending,i))
        {
            cout << "1";
        }
        else 
        {
            cout << "0";
        }
    }
    cout << endl;
}

int main()
{
    sigset_t nset,oset;
    //首先初始化
    sigemptyset(&nset);
    sigemptyset(&oset);
    //然后在将2号信号添加到指定的信号集中
    sigaddset(&nset,2);
    //然后设置信号屏蔽字为nset所指向的值
    sigprocmask(SIG_SETMASK,&nset,&oset);//这个时候就把2号信号屏蔽了

    sigset_t pending;
    int cnt = 0;
    while(true)
    {
        int n = sigpending(&pending);
        if(n < 0) continue;
        //打印信号集
        Printpending(pending);
        cnt++;
        sleep(1);
        //解除屏蔽
        if(cnt == 10)
        {
            sigprocmask(SIG_SETMASK,&oset,nullptr);//解除屏蔽
        }
    }
    return 0;
}

如上,当发送2号信号后,就会是未决状态,当第10秒的时候解除屏蔽2号信号,就会立即执行该信号,就会退出进程

相关文章:

  • 深入解析网络协议:从OSI七层模型到HTTP与TCP/IP的关系
  • 2安卓开发的主要语言
  • 【STM32】TIM输入捕获-学习笔记
  • 【北上广深杭大厂AI算法面试题】深度学习篇...这里详细说明ResNet中为什么不用dropout?
  • AI 大模型本身的(自己的)(如 GPT、BERT 等)的自动化测试
  • 网络安全学多久?就业前景如何?
  • 二、双指针——6. 三数之和
  • 探秘基带算法:从原理到5G时代的通信变革【四】Polar 编解码(二)
  • 大模型——CogView4:生成中英双语高清图片的开源文生图模型综合介绍
  • navicat下载与安装【带布丁】
  • 记录uniapp小程序对接腾讯IM即时通讯无ui集成(2)
  • 人工智能 全部技术栈以及简单运用场景
  • Springboot 循环依赖
  • Python学习第五天
  • 城市管理综合执法系统源码,B/S模式与手机等移动终端架构,java语言开发,可扩展性强
  • 基于 vLLM 部署 LSTM 时序预测模型的“下饭”(智能告警预测与根因分析部署)指南
  • 【学Rust写CAD】10 加法器
  • C++ Primer 动态数组
  • React面试葵花宝典之三
  • Gpt翻译完整版
  • 上海做兼职的网站/郑州网站推广效果
  • 建设自己的电影网站/免费网站制作软件平台
  • 上海网站建设规划/全网营销平台
  • 基础型网站套餐/福州seo网站推广优化
  • 织梦做的网站用什么数据库/网络推广图片大全
  • 搜索引擎作弊网站有哪些/国外网站开发