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

【Linux庖丁解牛】— 保存信号!

1. 信号其他相关常见概念

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

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

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

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

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

我们光知道这些概念还不够,我们接下来需要知道这些概念在系统内核中的具体体现,接下来我们来看看进程PCB中的三张表:

我们首先来看到pending表,这张表实际上就是unsigned int pending,就是一张位图【比特位的位置表示信号的编号,内容表示是否收到信号】,这张表的本质作用就是记录进程收到的信号

我们再来看到第二张表block表,这张表实际上也是unsigned int block,也是一张位图【比特位的位置表示信号的编号,内容表示是信号是否阻塞】。pending表和block表就可以决定一个进程的某个信号是否收到,是否阻塞,是否未决,是否递达。

最后我们看到第三张表handler表,该表本质上是函数指针数组,数组的类型为sighandler_t*,还记得我们的自定义信号处理方法吗:

该表的下标记录信号的编号,内容就是信号所对应的处理方式。所以:自定义信号处理方式的本质就是修改handler表!!

> 我们再来认识两个宏:SIG_IGN【ignore】 & SIG_DEL【default】

很好理解,SIG_IGN就是忽略处理的意思,ING_DEL就是恢复默认处理的意思。

下面简单写个代码使用一下:

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <functional>
#include <vector>void hander(int sig)
{std::cout<<"\nhello sig->"<<sig<<std::endl;signal(sig,SIG_DFL);std::cout<<"恢复默认处理动作\n";
}int main()
{signal(2,hander);//忽略处理// signal(2,SIG_IGN);while(true){printf("haha\n");sleep(1);}return 0;
}

2. 信号集及其操作函数

2.1 sigset_t

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

2.2 信号集操作函数

sigset_t类型对于每种信号用⼀个bit表示“有效”或“无效”状态,至于这个类型内部如何存储这些 bit则依赖于系统实现,从使用者的角度是不必关心的,使用者只能调用以下函数来操作sigset_t变量,而不应该对它的内部数据做任何解释,比如用printf直接打印sigset_t变量是没有意义的。

int sigemptyset(sigset_t *set); 
int sigfillset(sigset_t *set); 
int sigaddset(sigset_t *set, int signo); 
int sigdelset(sigset_t *set, int signo); 
int sigismember(const sigset_t *set, int signo);

• 函数sigemptyset初始化set所指向的信号集,使其中所有信号的对应bit清零,表示该信号集不含任何有效信号。

• 函数sigfillset初始化set所指向的信号集,使其中所有信号的对应bit置位,表示该信号集的有效信号 包括系统支持的所有信号。

• 注意,在使用sigset_t类型的变量之前,⼀定要调用sigemptyset或sigfillset做初始化,使信号集处于确定的状态。初始化sigset_t变量之后就可以在调用sigaddset和sigdelset在该信号集中添加或删除某种有效信号

这四个函数都是成功返回0,出错返回-1。sigismember是⼀个布尔函数,用于判断⼀个信号集的有效信 号中是否包含某种信号,若包含则返回1,不包含则返回0,出错返回-1。

> sigprocmask & sigpending 

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

 第一个参数可以传以下三个参数:

 第二个参数传我们设置的信号集即可,第三个参数是输出型参数,获取旧的信号集。

下面看一下sigpending

该函数的作用就是读取当前进程的未决信号集,set即输出型参数。

> demon代码

接下来我们来写一个demon代码来将上面的函数使用一下,先说一下带代码的具体逻辑:先把当前进程的2号信号屏蔽,然后把pending位图循环打印输出,我们不断发送信号就可以看到pending的变化。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <functional>
#include <vector>void Print(sigset_t &pending)
{printf("我是一个进程(0x%0x),pending:", getpid());for (int i = 31; i >= 1; i--){if (sigismember(&pending, i)){std::cout << 1;}elsestd::cout << 0;}printf("\n");
}int main()
{// 屏蔽信号sigset_t block;sigset_t old_block;// 初始化sigemptyset(&block);sigemptyset(&old_block);// 将2号信号设置进block中sigaddset(&block, 2);// 完成屏蔽sigprocmask(SIG_SETMASK, &block, &old_block);while (true){sigset_t pending;sigpending(&pending);Print(pending);sleep(1);}return 0;
}

现在,如果我们将所有的信号屏蔽,那么所有信号不都是无法被递达了吗??那这个进程不就是无法被杀掉了吗??其实不然,9号信号既无法被捕捉,也无法被屏蔽!!

现在,我们基于以上代码10秒解除对2号信号的屏蔽,然后我们希望看到2号信号被递达:

 这里还有一个问题:解除对2号信号的屏蔽之后,一旦2号信号被递达,那么pending是被立即修改【在处理动作完成之前】,还是在处理动作完成之后呢??做个实验便可得出结论了

预期结果:如果在捕捉动作中打印的pending 全为0,则pending是被立即修改【在处理动作完成之前】,否则,pending在处理动作完成之后修改。

3. core VS term

进程收到信号退出的方式有core和term两种,但这两种方式有什么区别呢??

core是核心意思,一旦进程以core方式退出,那么系统就会在当前路径下形成一个文件。进程异常退出的时候,会将进程在内存中存储的核心数据拷贝到磁盘中形成一个文件!!之后再进程退出,我们将这种机制叫做核心转储用来支持调试debug。而term就是直接进程退出。

我们之所以之前没有看到这个形成文件的行为,是因为在我们云服务器上默认是关闭这个机制的【原因:……】。

现在我们打开:

不过,为什么要有核心转储机制呢?? 

主要是为了支持debug调试,当我们的程序崩溃时,可以使用gdb命令中的core-file core快速定位到崩溃的代码!!!

说到这里,不知道各位是否还记得这张图

0表示进程正常退出,那是因为我们的信号只有1~31,而没有0号信号!!低7位记录信号,第八位记录是否core dump。

#include <iostream>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <functional>
#include <vector>
#include <wait.h>int main()
{pid_t id = fork();if (id == 0){printf("hahaha\n");printf("hahaha\n");printf("hahaha\n");printf("hahaha\n");int a = 10;a /= 0;printf("hahaha\n");printf("hahaha\n");printf("hahaha\n");}int status=0;int n=waitpid(id,&status,0);printf("signal->%d exit_code->%d core_dump->%d\n",status&0x7F,(status>>8)&0xFF,(status>>7)&0x1);return 0;
}


文章转载自:
http://bundook.hnsdj.cn
http://botulinus.hnsdj.cn
http://chlorite.hnsdj.cn
http://benzocaine.hnsdj.cn
http://approximative.hnsdj.cn
http://chessylite.hnsdj.cn
http://anenst.hnsdj.cn
http://am.hnsdj.cn
http://anlace.hnsdj.cn
http://attenuate.hnsdj.cn
http://calling.hnsdj.cn
http://bba.hnsdj.cn
http://armoric.hnsdj.cn
http://annulment.hnsdj.cn
http://bossism.hnsdj.cn
http://briony.hnsdj.cn
http://callosity.hnsdj.cn
http://antipyrotic.hnsdj.cn
http://chiffonade.hnsdj.cn
http://barathea.hnsdj.cn
http://anoscope.hnsdj.cn
http://arithmetician.hnsdj.cn
http://absinthium.hnsdj.cn
http://adulterer.hnsdj.cn
http://californiana.hnsdj.cn
http://celeriac.hnsdj.cn
http://back.hnsdj.cn
http://allsorts.hnsdj.cn
http://brutehood.hnsdj.cn
http://antagonise.hnsdj.cn
http://www.dtcms.com/a/281749.html

相关文章:

  • HTML网页结构(基础)
  • 【linux V0.11】init/main.c
  • 函数指针与指针函数练习讲解
  • 9、线程理论1
  • HostVDS 云服务器测评:平价入门、流媒体解锁全美、表现稳定
  • 暑假Python基础整理 --异常处理及程序调试
  • Redis 中的持久化机制:RDB 与 AOF
  • Java之Stream其二
  • 第二章 OB 存储引擎高级技术
  • 数学金融与金融工程:学科差异与选择指南
  • 【AI News | 20250714】每日AI进展
  • 为 Git branch 命令添加描述功能
  • 将 Vue 3 + Vite + TS 项目打包为 .exe 文件
  • 711SJBH构建制造业信息化人才培训体系的对策-开题报告
  • 21-C#的委托简单使用-1
  • Datawhale 25年7月组队学习coze-ai-assistant Task1学习笔记:动手实践第一个AI Agent—英伦生活口语陪练精灵
  • yolov5、yolov8、yolov11、yolov12如何训练及轻量化部署-netron-onnx
  • echarts折线图的 线条的样式怎么控制
  • Python os模块完全指南:从入门到实战
  • python编程实现GUI界面的排序与查找算法动态模拟演示程序
  • Sa-Token使用要点
  • mongoDB安装初始化及简单介绍
  • 2025/7/15——java学习总结
  • Pandas 和 NumPy 使用文档整理
  • 大宗现货电子盘交易系统核心功能代码解析
  • QT6 源,六章事件系统(8)QEvent 的孙子类:QEnterEvent 光标进入
  • 无穿戴动捕如何凭借摄像头视觉识别算法,赋能高校专业教学革新?
  • python进阶
  • 145-变分模态分解VMD与平稳小波变换SWT信号降噪算法实现!
  • 4G模块 A7680通过MQTT协议连接到onenet(新版)