【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号信号, 变为00000000000000010sigset_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号信号, 变为00000000000000010sigset_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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

指针的深入了解2

1.const修饰指针 在这之前我们还学过static修饰变量&#xff0c;那我们用const来修饰一下变量会有什么样的效果呢&#xff1f; 我们来看看&#xff1a; 我们可以看到编译器报错告诉我们a变成了一个不可修改的值&#xff0c;我们在变量前加上了const进行限制&#xff0c;但是我…

使用py-spy对python程序进行性能诊断学习

py-spy简介 py-spy是一个用Rust编写的轻量级Python分析工具&#xff0c;它能够监视正在运行的Python程序&#xff0c;而不需要修改代码或者重新启动程序。Py-spy可以在不影响程序运行的情况下&#xff0c;采集程序运行时的信息&#xff0c;生成火焰图&#xff08;flame graph&…

linux 基于科大讯飞的文字转语音使用

官方文档地址&#xff1a;离线语音合成 Linux SDK 文档 | 讯飞开放平台文档中心 一、SDK下载 1、点击上面官方文档地址的链接&#xff0c;可以跳转到以下界面。 2、点击“普通版”&#xff0c;跳转到以下界面。 3、点击“下载”跳转到以下界面 4、最后&#xff0c;点击“SDK下…

Qt6入门教程 12:QAbstractButton

目录 一.状态 二.信号 三.使用 1.自定义按钮 2.多选 3.互斥 QAbstractButton类实现了一个抽象按钮&#xff0c;并且让它的子类来指定如何处理用户的动作&#xff0c;并指定如何绘制按钮。QAbstractButton类是所有按钮控件的基类。 QAbstractButton提供…

【阿里云服务器数据迁移】 同一个账号 不同区域服务器

前言 假如说一台云服务器要过期了,现在新买了一台,有的人会烦恼又要将重新在新的服务器上装环境,部署上线旧服务器上的网站项目, 但是不必烦恼,本文将介绍如何快速将就旧的服务器上的数据迁移到新的服务器上. 包括所有的环境和网站项目噢 ! 步骤 (1) 创建旧服务器自定义镜像…

Linux命令 - 统计log日志某接口用户访问频次并排序

​ 背景 某天发现内部人员使用的app服务器访问突增&#xff0c;但不影响服务正常运行&#xff0c;想通过log统计接口的人员访问频次。 从监控平台可以看到访问激增的接口&#xff0c;因Nginx不缓存用户信息只有访问IP&#xff0c;日志清洗的Hive表只能访问前一天&#xff0c;…

行测-资料:2. 一般增长率、增长量

1、一般增长率 1.1 百分数和百分点 50%&#xff0c;20% 1.2 增长率和倍数 1.5&#xff1b;50 1.3 成数和翻番 1.4 增幅&#xff0c;降幅&#xff0c;变化幅度 A&#xff0c;A&#xff0c;D B&#xff0c;高于全国增速 2.3 个百分点&#xff0c;21.8 - 2.3 19.5。 5%&#xff0…

打开 IOS开发者模式

前言 需要 1、辅助设备&#xff1a;苹果电脑&#xff1b; 2、辅助应用&#xff1a;Xcode&#xff1b; 3、准备工作&#xff1a;苹果手机 使用数据线连接 苹果电脑&#xff1b; 当前系统版本 IOS 17.3 通过Xcode激活 两指同时点击 Xcode 显示选择&#xff0c;Open Develop…

重生奇迹MU平民玩家推荐的职业

女魔法师 女魔法师是一个非常适合平民玩家的职业选择。她拥有着强大的魔法攻击能力&#xff0c;可以轻松地击败敌人。而且女魔法师的装备价格相对较低&#xff0c;适合玩家们的经济实力。 精灵射手 精灵射手是一个非常灵活的职业选择。他们可以远程攻击&#xff0c;可以在战…

使用PCL进行法向量可视化

使用PCL进行法向量可视化 文章目录 1、使用PCL进行法向量可视化2、计算所有点的法线并显示3、计算一个子集的法线 1、使用PCL进行法向量可视化 #include <iostream> #include <pcl/io/pcd_io.h> #include <pcl/visualization/pcl_visualizer.h> #include &l…

element plus使用问题

文章目录 element plusvue.config.js注意1、有时候会报错 not a function2、使用 ElMessage 报错3、 element plus 版本过高4、警告Feature flag VUE_PROD_HYDRATION_MISMATCH_DETAILS is not explicitly defined.5、报错 ResizeObserver loop completed with undelivered noti…

Redis高级应用

文章目录 1.5.1 布隆过滤器BloomFilter1.5.1.1 原理1.5.1.2 使用场景 1.5.2 Redis分布式锁1.5.2.1 使用案例分析1.5.2.1.1 单机版没加锁1.5.2.1.2 单节点Redis实现分布式锁1.5.2.1.3 集群下的分布式及CAP1.5.2.1.4 Redisson可靠分布式锁1.5.2.1.5 Redis分布式锁-Redlock算法1.5…

Linux之进程间通信(system V 共享内存)

目录 一、共享内存 1、基本原理 2、共享内存的创建 3、共享内存的释放 4、共享内存的关联 5、共享内存的去关联 6、查看IPC资源 二、完整通信代码 三、共享内存的特点 四、信号量 1、相关概念 2、信号量概念 进程间通信的本质就是让不同的进程看到同一个资源。而前…

微信小程序(二十)Vant组件库的配置

教程很详细&#xff0c;直接上过程 上一篇 官方文档也有&#xff0c;但是因为版本的更新&#xff0c;官方文档并没有跟着改变&#xff0c;这里我写一份最新版能用的教程 &#xff08;口头禅还是不能少的&#x1f923;&#x1f923;&#x1f923;&#xff09; 灵魂拷问&#xf…

Android Studio 提示Use app:drawableStartCompat instead of android:drawableStart

每次提交代码时&#xff0c;AS这个老妈子总爱唠叨一堆warning&#xff0c;这些Warning都在讲什么&#xff1f; 1.Use app:drawableStartCompat instead of android:drawableStart 在Android开发中&#xff0c;android:drawableStart和app:drawableStartCompat是两个用于设置…

基于YOLOv8的摄像头吸烟行为检测系统(Python源码+Pyqt6界面+数据集)

&#x1f4a1;&#x1f4a1;&#x1f4a1;本文主要内容:详细介绍了摄像头下吸烟行为检测系统&#xff0c;在介绍算法原理的同时&#xff0c;给出Pytorch的源码、训练数据集以及PyQt6的UI界面。在界面中可以选择各种图片、视频进行检测识别&#xff0c;可进行置信度、Iou阈值设定…

智能分析网关V4智慧冶金工厂视频智能监管方案

一、背景与需求 随着工业4.0的推进&#xff0c;冶金行业正面临着转型升级的压力。为了提高生产效率、降低能耗、保障安全&#xff0c;冶金智能工厂视频监管方案应运而生。该方案通过高清摄像头、智能分析技术、大数据处理等手段&#xff0c;对工厂进行全方位、实时监控&#xf…

详细解读vcruntime140_1.dll修复的手段,如何快速解决vcruntime140_1.dll丢失问题

当出现“无法找到vcruntime140_1.dll”或程序“未能正常启动”时&#xff0c;这通常指示系统中缺失了一个关键文件&#xff1a;vcruntime140_1.dll。作为Visual C Redistributable组件的一部分&#xff0c;这个小文件在很多用Visual Studio编译的C程序运行时发挥着重要作用。解…

Qslog开源库使用

Qslog源码下载地址&#xff1a;https://github.com/victronenergy/QsLog 1.QSLOG使用方式 &#xff08;1&#xff09;源码集成 在你的工程中&#xff0c;直接包含QsLog.pri文件&#xff0c;进行源码集成。当然你也可以包含QsLog.pri后&#xff0c;编译为xx.dll&#xff0c;在…

MVCC原理讲解(深入浅出)

目录 一、什么是MVCC 二、当前读、快照读都是什么鬼 三、当前读 四、快照读 五、数据库的并发场景 六、MVCC解决并发的哪些问题 1.解决问题如下&#xff1a; 七、MVCC的实现原理 1.版本链 八、undo日志 1.undo log 的用途 2.undo log主要分为两种 九、Read View…