【Linux】第三十七站:信号保存

文章目录

  • 一、信号发送
  • 二、信号保存
    • 1.为什么要进行信号保存?
  • 三、阻塞信号
    • 1.信号的一些相关概念
    • 2.在内核中的表示
    • 3.sigset_t
    • 4.信号集操作函数
    • 5.sigprocmask
    • 6.sigpending
    • 7. 总结

一、信号发送

如下所示,对于普通信号,它的编号是从1~31。这个是比较像一个int的大小的

image-20240126182909121

对于普通信号而言,对于进程而言。关心的是自己有还是没有信号,收到的是哪一个信号。

这个是给进程的PCB发的

也就是说里面有这样一个字段

task_struct{
	int signal; // 0000 0000 0000 0000 0000 0000 0000 0000
}	

如果给进程发的是一号信号,那么则将第一位给置为1。(注意这里有第0位)

如果是二号信号,则将第二位置为1即可。

所以描述一个信号,用比特位的位置来表示,即普通信号是用位图来管理信号

即:

  1. 比特位的内容是0还是1,表明是否收到
  2. 比特位的位置(第几个),表示信号的编号
  3. 所谓的“发信号”,本质就是OS去修改task_struct的信号位图对应的比特位。也就是写信号

为什么必须是OS去写呢?

因为OS是进程的管理者,只有它有资格才能修改task_struct内部的属性

为什么操作系统不直接把这个进程干掉,而要先给个信号?先修改下位图?

因为底层并不清楚上层在做什么。如果上层的一些收尾工作,没有被处理,就出问题了。

二、信号保存

1.为什么要进行信号保存?

进程收到信号之后,可能不会立即处理这个信号。

信号不会被处理,就要有一个时间窗口。

对于普通信号,它用的是位图,只要收到就会先保存,但是如果这个信号还没处理,就又来个信号。那么就只记得最近一次的信号。

而对于实时信号。

只要发送了,就要立即处理,哪怕此时进程在忙。它的用的是双向链表。

三、阻塞信号

1.信号的一些相关概念

  • 实际执行信号的处理动作称为信号递达(Delivery)
  • 信号从产生到递达之间的状态,在位图当中保存着,还没有被处理。称为信号未决(Pending)
  • 进程可以选择阻塞(Block)某个信号
  • 被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作
  • 注意:阻塞和忽略是不同的,只要信号被阻塞就不会被递达,而忽略是在递达之后可选的一种处理动作

2.在内核中的表示

如下图所示

image-20240126191746658

进程PCB中有三张表,两个位图和一个函数指针表

对于block表,它表示的是如果是1,该信号是被阻塞的,0没有被阻塞。

这个pending表里面就是前面所提到的,信号保存的表。发送信号后,就会修改这张表,即表示是否收到信号。

信号的范围是[1,31],每一种信号都要有自己的一种处理方法。这个handler就是指向一个一个的信号处理的方法。系统里面也有默认的方法,用户也可以给他提供一个方法。修改该数组下标的内容。也就是用signal去修改

image-20240126192156904

image-20240126193004387

  • 每个信号都有两个标志位分别表示阻塞(block)和未决(pending),还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。在上图的例子中,SIGHUP信号未阻塞也未产生过,当它递达时执行默认处理动作。
  • SIGINT信号产生过,但正在被阻塞,所以暂时不能递达。虽然它的处理动作是忽略,但在没有解除阻塞之前不能忽略这个信号,因为进程仍有机会改变处理动作之后再解除阻塞。
  • SIGQUIT信号未产生过,一旦产生SIGQUIT信号将被阻塞,它的处理动作是用户自定义函数sighandler。如果在进程解除对某信号的阻塞之前这种信号产生过多次,将如何处理?POSIX.1允许系统递送该信号一次或多次。Linux是这样实现的:常规信号在递达之前产生多次只计一次,而实时信号在递达之前产生多次可以依次放在一个队列里。我们不讨论实时信号
  • 对于信号它的一切操作都离不开这三张表

如果我们要操作这两张位图,我们从技术角度可以直接用位运算来实现。

当然操作系统会直接提供一些接口。

让我们弄一张位图,然后带进去,带出去即可

不过,系统害怕我们的位图可能被扩展了等等问题,所以操作系统专门提供了一个位图类型。

对于这个函数指针表,我们需要了解一下SIG_IGN和SIG_DFL

image-20240126201817543

image-20240126201839067

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main()
{
signal(2, SIG_IGN);
while(1)
{
  cout << "hello signal" << endl;
  sleep(1);
}
return 0;
}

运行结果为

image-20240126202026027

如果是默认的

#include <iostream>
#include <signal.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>

using namespace std;

int main()
{
signal(2, SIG_DFL);
while(1)
{
  cout << "hello signal" << endl;
  sleep(1);
}
return 0;
}

运行结果为

image-20240126202130729

正常终止了

3.sigset_t

每个信号只有一个bit的未决标志,非0即1,不记录该信号产生了多少次,阻塞标志也是这样表示的。

因此,未决和阻塞标志可以用相同的数据类型sigset_t来存储,sigset_t称为信号集,这个类型可以表示每个信号的“有效”或“无效”状态,在阻塞信号集中“有效”和“无效”的含义是该信号是否被阻塞,而在未决信号集中“有效”和“无效”的含义是该信号是否处于未决状态。

阻塞信号集也叫做当前进程的信号屏蔽字(Signal Mask),这里的“屏蔽”应该理解为阻塞而不是忽略。

4.信号集操作函数

#include <signal.h>
//初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不包含任何有效信号。
int sigemptyset(sigset_t *set);
//初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号包括系统支持的所有信号。
int sigfillset(sigset_t *set);
//使得set信号集中的signo信号置位
int sigaddset (sigset_t *set, int signo);
//使得set信号集中的signo信号清零
int sigdelset(sigset_t *set, int signo);
//判断set中是否有signo信号,若包含则返回1,不包含则返回0,出错返回-1
int sigismember(const sigset_t *set, int signo);
  • **注意,在使用sigset_ t类型的变量之前,一定要调 用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号。 **
  • **前四个函数都是成功返回0,出错返回-1。sigismember是一个布尔函数,用于判断一个信号集的有效信号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。 **

5.sigprocmask

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

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
//返回值:若成功则为0,若出错则为-1
//第二个是输入型参数,第三个是输出型参数

如果oset是非空指针,则读取进程的当前信号屏蔽字通过oset参数传出。

如果set是非空指针,则更改进程的信号屏蔽字,参数how指示如何更改。如果oset和set都是非空指针,则先将原来的信号 屏蔽字备份到oset里,然后
根据set和how参数更改信号屏蔽字。假设当前的信号屏蔽字为mask,下表说明了how参数的可选值

对于how参数,有以下三个选项。

image-20240126204244128

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

6.sigpending

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

我们接下来先用下面的样例

#include <iostream>
#include <signal.h>
#include <unistd.h>

using namespace std;
void PrintPending(const sigset_t& pending)
{
    for(int signo = 31; signo >= 1; signo--)
    {
        cout << sigismember(&pending, signo);
    }
    cout << endl << endl;
}

int main()
{
    //1.先对2号信号进行屏蔽
    sigset_t bset, oset;
    sigemptyset(&bset);
    sigaddset(&bset, 2);  //此时还没有设置进入到进程的task_struct

    //调用系统调用,将数据设置进内核
    sigprocmask(SIG_SETMASK, &bset, &oset);//屏蔽了2号信号了

    //2.重复打印当前进程的pending 000000000000000
    //发送2号信号, 变为00000000000000010
    
    sigset_t pending;
    while(true)
    {
        //获取
        int n = sigpending(&pending);
        //打印
        if(n < 0) continue;
        PrintPending(pending);
        sleep(1);
    }

    return 0;
}

这个代码的功能是屏蔽2号信号,然后我们重复打印pending表。运行结果如下所示。

image-20240126211941298

我们再看一下下面的代码

这段代码在上面的基础上加了解除屏蔽2号信号

#include <iostream>
#include <signal.h>
#include <unistd.h>

using namespace std;
void PrintPending(const sigset_t& pending)
{
    for(int signo = 31; signo >= 1; signo--)
    {
        cout << sigismember(&pending, signo);
    }
    cout << endl << endl;
}
void handler(int signo)
{
    cout << "catch a signo:" << signo << endl;
    //exit(1);
}
int main()
{
    signal(2, handler);

    //1.先对2号信号进行屏蔽
    sigset_t bset, oset;
    sigemptyset(&bset);
    sigaddset(&bset, 2);  //此时还没有设置进入到进程的task_struct

    //调用系统调用,将数据设置进内核
    sigprocmask(SIG_SETMASK, &bset, &oset);//屏蔽了2号信号了

    //2.重复打印当前进程的pending 000000000000000
    //发送2号信号, 变为00000000000000010
    
    sigset_t pending;
    int cnt = 0;
    while(true)
    {
        //获取
        int n = sigpending(&pending);
        //打印
        if(n < 0) continue;
        PrintPending(pending);
        sleep(1);
        cnt++;
        //解除屏蔽
        if(cnt == 20)
        {
            cout << "unblock 2 signo" << endl;
            sigprocmask(SIG_SETMASK, &oset, nullptr);
        }
    }

    return 0;
}

运行结果为

image-20240126212909115

如果将所有的信号全部屏蔽掉,那是不是信号就不会被处理了?

我们能想到的,操作系统肯定考虑到了,肯定有一些信号是无法被屏蔽的。

9号和19号不可被屏蔽,也不可被捕捉

我们可以用下面代码来验

#include <iostream>
#include <signal.h>
#include <unistd.h>

using namespace std;
void PrintPending(const sigset_t& pending)
{
    for(int signo = 31; signo >= 1; signo--)
    {
        cout << sigismember(&pending, signo);
    }
    cout << endl << endl;
}
void handler(int signo)
{
    cout << "catch a signo:" << signo << endl;
    //exit(1);
}

int main()
{
    sigset_t bset, oset;
    sigemptyset(&bset);
    sigemptyset(&oset);

    for(int i = 1; i <= 31; i++)
    {
        sigaddset(&bset, i);
    }
    sigprocmask(SIG_SETMASK, &bset, &oset);
    sigset_t pending;
    while(true)
    {
        int n = sigpending(&pending);
        if(n < 0) continue;
        PrintPending(pending);
        sleep(1);
    }
}

下面是部分验证结果,其余可自行验证

image-20240126214602217

7. 总结

我们可以用下图来总结我们前面的关系

image-20240126215152805

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/353918.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

N-141基于springboot,vue网上拍卖平台

开发工具&#xff1a;IDEA 服务器&#xff1a;Tomcat9.0&#xff0c; jdk1.8 项目构建&#xff1a;maven 数据库&#xff1a;mysql5.7 系统分前后台&#xff0c;项目采用前后端分离 前端技术&#xff1a;vueelementUI 服务端技术&#xff1a;springbootmybatis-plusredi…

九州金榜|为什么鼓励式家庭教育?

鼓励式教育是一种积极的教育方式&#xff0c;它强调通过鼓励和肯定来激发孩子的积极性和自信心&#xff0c;帮助孩子更好地成长和发展。在家庭教育中&#xff0c;鼓励式教育同样具有重要意义。九州金榜家庭教育和大家一起探讨关于鼓励式教育的好处以及意义&#xff1a; 一.有助…

JRT实体比对

之前已经实现了JRT实体编译的菜单&#xff0c;用Linux指令编译放在网站下的实体代码。那么就有个问题&#xff0c;有人就是直接换实体jar文件不修改网站的实体代码。或者就只修改实体代码不编译搁置在那里&#xff0c;那么后面更新实体的人就得给别人背锅&#xff0c;后面人新编…

机器学习 | 如何使用 Seaborn 提升数据分析效率

Seaborn和Matplotlib都是Python可视化库&#xff0c;它们都可以用于创建各种类型的图表。但是&#xff0c;Seaborn 和Matplotlib在概念和设计上有一些不同。 Matplotlib虽然已经是比较优秀的绘图库了&#xff0c;但是它有个今人头疼的问题&#xff0c;那就是API使用过于复杂&am…

Blender教程(基础)-物体的移动、旋转与缩放-04

一、新建一个立方体 ShiftA新建一个立方体用来演示。 二、物体的移动 xyz轴移动 点击下图图左侧的移动选项后&#xff0c;选中要移动的物体&#xff0c;会出现三个箭头的方向&#xff0c;这分别代表沿着x、y、z轴移动。xyz平面移动 这个小正方体代表沿着某一个面移动&#…

04.领域驱动设计:了解聚合和聚合根,怎样设计聚合

目录 1、概述 2、聚合 3、聚合根 4、怎么设计聚合 4.1 聚合的构建过程主要步骤 第 1 步&#xff1a;采用事件风暴。 第 2 步&#xff1a;选出聚合根。 第 3 步&#xff1a;找出与聚合根关联的所有紧密依赖的实体和值对象。 第 4 步&#xff1a;画出对象的引用和依赖模型…

vue3框架基本使用

一、安装包管理工具 vite和vue-cli一样&#xff0c;都是脚手架。 1.node版本 PS E:\vuecode\vite1> node -v v18.12.12.安装yarn工具 2.1 yarn简单介绍 yarn是一个包管理工具&#xff0c;也是一个构建、打包工具 yarn需要借助npm进行安装&#xff1a;执行的命令行npm i…

找不同-《企业应用架构模式》2024典藏版

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 以下是2004年《企业应用架构模式》中译本和2024年《企业应用架构模式》典藏版译本的页面。 您能从中找出至少10处不同吗&#xff1f; 如何选择UMLChina服务 UMLChina公众号精选&…

Blender教程(基础)-面的细分与删除、挤出选区-07

一、Blender之面的细分 新建一个立方体&#xff0c;在编辑模式下、选中一个面。 在选中的面上单击右键弹出细分选项&#xff0c;选择细分。 在选中细分后、会默认细分1次。修改细分次数在左下角 二、Blender之面的删除 选择中需要操作的面&#xff0c;在英文状态下按X键弹…

VSCode 1.85.0更新的3个实用功能

1、单个文件可直接拖拽为独立窗口 当单文件过长&#xff0c;直接分成两个视图就不用上下频繁滚动 2、将终端移动到编辑器区域 此时&#xff0c;终端也可像文件一样拖拽为独立窗口 3、文件夹目录粘性头部 默认关闭&#xff0c;需要设置 "workbench.tree.enableStickyScro…

Linux权限的概念,shell命令以及运行原理

目录 1.shell命令以及运行原理2.Linux权限2.1Linux中的两类用户2.2Linux权限管理2.2.1文件访问者的分类&#xff08;人&#xff09;2.2.2文件类型和访问权限&#xff08;事物属性&#xff09;2.2.3文件的类型以及权限的缩写2.2.4文件权限值的表示方法2.2.5文件访问权限的相关设…

HTML 曲线图表特效

下面是代码 <!doctype html> <html> <head> <meta charset"utf-8"> <title>基于 ApexCharts 的 HTML5 曲线图表DEMO演示</title><style> body {background: #000524; }#wrapper {padding-top: 20px;background: #000524;b…

opencv#33 边缘检测

边缘检测原理 图像的每一行每一列都可以看成是一个连续的信号经过离散后得到的数值&#xff0c;例如上图左侧给出的图像由黑色到白色的一个信号&#xff0c;也就是图像中某一行像素变化是由黑色逐渐到白色&#xff0c;我们将其对应在一个坐标轴中&#xff0c;将像素值的大小对应…

sysfs: cannot create duplicate filename ‘/devices/virtual/leds/led1‘问题查找及解决

问题描述&#xff1a;安装模块时出现如下错误&#xff1a; [rootVinxin_PC leds]# cd /driver_Test/ [rootVinxin_PC driver_Test]# ls app leds-s5pv210.ko [rootVinxin_PC driver_Test]# lsmod Module Size Used by Not tainted [rootVin…

最全全国十七个数据入表和资产化案例深度解析

2024年1月1日起&#xff0c;财政部会计司发布的《企业数据资源相关会计处理暂行规定》正式施行&#xff0c;规定为数据资源的会计处理提供了明确的指导原则。这一里程碑事件也标志着我国在数据资产入表领域正式进入实际操作阶段&#xff0c;随后&#xff0c;数据资产入表在全国…

stable diffusion学习笔记——文生图(一)

模型设置 基本模型 基本模型也就是常说的checkpoint&#xff08;大模型&#xff09;&#xff0c;基本模型决定了生成图片的主体风格。 如上图所示&#xff0c;基本模型的后缀为.safetensors。需要存放在特定的文件夹下。 如果用的是启动器&#xff0c;可以在启动器内直接下载。…

2024年黑龙江省安全员C证证考试题库及黑龙江省安全员C证试题解析

题库来源&#xff1a;安全生产模拟考试一点通公众号小程序 2024年黑龙江省安全员C证证考试题库及黑龙江省安全员C证试题解析是安全生产模拟考试一点通结合&#xff08;安监局&#xff09;特种作业人员操作证考试大纲和&#xff08;质检局&#xff09;特种设备作业人员上岗证考…

(java)idel中将对与json的相互转

1、目录结构 2、导入包 在模块下面建立lib目录将包导入模块中 包的百度网盘 链接&#xff1a;https://pan.baidu.com/s/1abNF8cOTeNb00rM7tp04iQ?pwd39wc 提取码&#xff1a;39wc 3、建立两个测试类person和dog类 public class Dog {private String name;private int age…

STM32标准库——(5)EXTI外部中断

1.中断系统 中断&#xff1a;在主程序运行过程中&#xff0c;出现了特定的中断触发条件&#xff08;中断源&#xff09;&#xff0c;使得CPU暂停当前正在运行的程序&#xff0c;转而去处理中断程序&#xff0c;处理完成后又返回原来被暂停的位置继续运行 中断优先级&#xff…

CMake 完整入门教程(一)

1 前言 每一次学习新东西都是很有乐趣的&#xff0c;虽然刚开始会花费时间用来学习&#xff0c;但是实践证明&#xff0c;虽然学习新东西可能会花费一些时间&#xff0c;但是它们带来的好处会远远超过这些花费的时间。学习新东西是值得的&#xff0c;也是很有乐趣的。 网络上…