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

程序的“烽火台”:信号的产生与传递

一、认识信号

1. 什么是信号?

在了解进程信号之前,我们先用生活中的例子来理解一下。

在日常生活中,有许多信号,比如:红绿灯,鸣笛声,上下课铃声…,这些信号在没有产生之前,我们就已经知道了这些信号应该怎么处理,这是因为我们已经形成了一种共识。在现实世界中,为什么要有这些信号呢?我们用最简单的信号通知机制,告诉人,应该要做什么,维持秩序,是用来做信息事件通知的

大家都点过外卖吧,当外卖员给你打电话下楼取餐时,你不一定会立即下楼,所以,在进程中也是一样的,当信号来临时,进程也不一定立即处理信号

2. 进程是如何处理信号的?

那么,进程是如何处理信号的呢

1.默认动作

2.忽略信号

3.自定义捕捉

那么,进程是如何识别这些信号以及处理的呢

这是因为,进程内部已经内置了对于信号的识别和处理机制

man 7 signal //查看进程信号

在这里插入图片描述
在这里插入图片描述

1-31 是普通信号,34-64 是实时信号,这些信号都是宏。我们只研究普通信号。

进程未来要对信号做出处理,那么OS就要提供对应的系统调用,修改进程对于信号的处理动作

typedef void (*sighandler_t)(int);
//signum信号编号
//handler信号处理方法
//SIG_IGN 忽略信号
//SIG_DFL 默认动作
sighandler_t signal(int signum, sighandler_t handler)

以前我们使用 Ctrl + c 终止过前台进程,我们就以它为例,来验证一下。

在这里插入图片描述
在这里插入图片描述

可以看到,Ctrl + c 不再有效,这是因为我们将进程对于2号信号进行了忽略处理,如果我们使用默认处理,那么就会终止进程

在这里插入图片描述
在这里插入图片描述

自定义捕捉

在这里插入图片描述
在这里插入图片描述

handler函数会将发送的信号当做参数传入,执行自定义的方法

Ctrl + \ 向目标进程发送3号信号

还记得我们之前说过的前台进程后台进程吗?那么,什么是前台进程和后台进程呢

命令行启动的进程默认叫做前台进程 status+

./cmd &,让该进程进入后台运行 status

Linux一次登录状态的时候,OS会启动 bash 进程,由bash进程启动其他进程,但是系统任何时刻只允许一个进程处在前台,其它进程处在后台

计算机的键盘只有一个,允许获取键盘输入数据的进程,叫做前台进程

后台进程无法从键盘获取数据

这也是为什么我们无法使用 Ctrl + c 杀掉后台进程的原因,因为后台进程无法获取键盘数据

孤儿进程会自动把自己变成后台进程

在这里插入图片描述

二、信号产生

1. 产生信号的方式

1.键盘可以向目标进程发送信号

2.kill 命令向目标进程发送信号

3.系统调用

4.软件条件

5.异常

上面我们说过,进程处理信号可能不会立即处理,那么,进程在合适的时间处理信号时,就要求进程必须把信号进行保存

这就像你在宿舍打游戏时,外卖员给你打电话让你下楼取餐,你说等一会下去取餐,这就要求你的大脑必须将这个信息记住。

那么,进程将信号记录在哪里的呢又是怎么记录的

还记得PCB吗,PCB是描述进程的属性的,信号就被保存在进程的PCB里

那它又是怎么记录的呢使用位图就可以了(unsigned int bitmaps)一共31个信号,使用位图刚好合适

bit 的位置:表示信号编号

bit 的内容:表示是否收到

问题1:如何理解给进程发送信号

只需要修改进程的 task_struct,信号位图的特定位置由 0 置 1即可,本质就是向目标进程写信号

问题2:进程如何部分识别信号

通过位图对应的位置,是0还是1

可是PCB是内核的数据结构对象,修改位图本质是修改内核数据,那么,只能由OS来修改了

所以,无论信号发送的方式有多少种,最终全部都是由OS向目标进程发送信号的

我们说,进程对于信号的处理方式有3种,默认行为,忽略和自定义捕捉。

那么,如果我们将所有的信号都进行忽略和自定义捕捉呢?是不是所有的信号都失效了呢

接下来就验证一下。

自定义捕捉

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

忽略信号

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到,进程信号大部分都被捕捉和忽略,少部分信号是无法被捕捉和忽略的

2. 详解信号产生的方式

2.1 系统调用

信号的产生有多种方式,前面两种我们已经了解,现在就来看系统调用。

//成功返回0,失败返回-1
//pid目标进程
//sig发送的信号
int kill(pid_t pid, int sig);

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

//成功返回0,失败返回非0
int raise(int sig); 

在这里插入图片描述
在这里插入图片描述

void abort(void); //通常用来结束任务

在这里插入图片描述
在这里插入图片描述

我们稍微修改一下代码,看看运行结果。

在这里插入图片描述
在这里插入图片描述

可以看到,abort 函数向进程发送了6号信号,本应该执行自定义捕捉方法,但是执行完自定义捕捉方法之后依然执行了 abort 函数的功能

2.2 软件条件

软件条件

//返回先前设置的闹钟剩余的秒数,如果之前没有设置过闹钟,返回0
unsigned int alarm(unsigned int seconds);

在这里插入图片描述
在这里插入图片描述

需要注意的是,这个闹钟只会起一次效果

在这里插入图片描述

想让闹钟多次起效果就要求设置多个闹钟

在这里插入图片描述
在这里插入图片描述

alarm调用,一次只运行一个进程,设置一个闹钟,以最新的为准。第二次设置闹钟的新时间,会取消上一次的闹钟,返回上一次闹钟的剩余时间

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

alarm 有 IO 的时候,效率比较低

在这里插入图片描述
在这里插入图片描述

printf 不断与外设进行 IO,导致效率低下

在这里插入图片描述
在这里插入图片描述

通过对比,就可以验证上述结论了。

2.3 异常

比如说,野指针,除0错误会导致进程崩溃,那么,为什么这些错误会导致进程崩溃呢

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

可以看到,除0和野指针的确导致进程崩溃了,但是,信号自定义捕捉以后,进程在显示器上进行了刷屏,这是为什么呢

回答这个问题之前,我们先回答刚才的问题。

所以除0,野指针…导致进程崩溃的原因是因为软件问题被OS识别,给目标进程发送了信号,然后进程处理信号,默认终止了进程

现在,我们需要了解异常的底层再对刚才的问题进行回答。

除0

在进行除0时,这个工作是由CPU来完成的,CPU内会有许多寄存器,在运算时从内存中将这两个数据拿到两个寄存器中,比如eax中存放数据10,ebx中存放0进行除法运算,将运算结果写入到ecx中。在数学上,一个有限的数除以一个很小的数,会得到一个无穷大的数,而CPU内寄存器的位数是有限的,为了避免这种情况,在CPU内会存在一种控制和状态寄存器(EFLAGS),这个寄存器中会记录CPU单次运算所对应的状态,寄存器中有许多 bit 位,每一个 bit 位都表明一种标志,其中一个标志叫做溢出标志位在进行常规运算时,这个 bit 位的内容为0,表明结果可信,如果除0操作,溢出标志位的内容设置为1,结果不可信,表明本次运算在硬件上就报错了,那么,OS要不要知道呢?答案是要的OS是软硬件资源的管理者

除0操作,是CPU在执行代码的时候,这表明CPU一定在调度某个进程。当调度执行一个进程的时候CPU内部寄存器的本质是:当前进程的硬件上下文

OS已经知道了硬件报错了,那么OS知不知道是谁导致当前CPU报错的答案是知道的。在OS内有一个 struct task_struct* current 指针指向当前正在调度的进程

硬件报错,OS就会根据 current 指针找到目标进程,向目标进程发送信号,进程一旦默认被杀掉,当前进程的硬件上下文也就不存在了,CPU的报错也就没有了所以,OS杀掉进程是为了恢复CPU的正常工作

所以,刚才我们对信号进行了捕捉,为什么会在显示器上进行刷屏呢

自定义捕捉之后,我们没有将该进程进行退出,那么,该进程的PCB就会一直保留,硬件上下文也会一直存在,所以,OS会再一次调度该进程,恢复上下文,就会继续报错。只要该进程没有退出,就会被OS一直调度

野指针

CPU调度进程时,会从寄存器中获取指令,EIP指向当前指令的下一条指令的虚拟地址,IR寄存器根据EIP寄存器里的指令地址获取对应的指令内容,CR3寄存器保存的是页表的基地址,CPU根据EIP获取下一条指令的地址,MMU会拿着IR寄存器里的地址经过页表的转化得到物理地址,如果成功了,就继续向下执行,如果失败了,CPU里有一个CR2寄存器,存储触发页错误的线性地址(虚拟地址),触发页错误(硬件中断)

野指针问题如果触发了报错,本质也是触发了硬件CPU报错

野指针不一定会报错,比如数组越界

如何理解键盘产生信号呢?

键盘也是硬件,按下键盘的时候,OS也要知道,比如按下了 Ctrl + c,OS就会根据按键进行识别,向目标进程发送对应的信号

那么,OS是怎么知道键盘被按下了?

通过硬件中断做到的

键盘与CPU之间间接的通过针脚达到高低电平,从而触发硬件中断,向OS发送数据

那如果是磁盘,网卡呢?

所以,几乎每一种外设,都要在内核中内置一些处理方法,来进行处理中断的请求。这些方法构成一张中断向量表,也是OS的一部分

3. 核心转储

现在,来探讨一下CoreTerm的区别。

在这里插入图片描述

在这里插入图片描述

core dump标志位,叫做核心转储,该标志位表示退出信号的详细退出类型。0表示Term,1表示Core(进一步追踪进程异常原因)

进程异常了,我们肯定想知道为什么会异常,哪一行导致异常的。

什么叫做核心转储呢

OS在进程结束的时候,把进程当前运行的上下文数据,dump转而存储在当前目录下,形成一个core文件,可以用它来进行调试

core文件在linux中默认是被禁掉的

ulimit -a //显示所有资源限制
(gdb) core-file core //加载core文件,定位出错行

在这里插入图片描述

今天的文章分享到此结束,觉得不错的小伙伴给个一键三连吧。

http://www.dtcms.com/a/350851.html

相关文章:

  • 【基础-单选】使用http发起网络请求,需要以下哪种权限?
  • C6.2:小信号、交流电流增益分析
  • 立轴式小型混凝土搅拌机的设计含14张CAD
  • 客户生命周期价值帮助HelloFresh优化其营销支出
  • 快速了解工业相机中的连续采集、软触发、硬触发和同步触发以及PTP同步触发
  • Spring介绍
  • Linux iptables 防火墙
  • Linux网络编程基础API
  • [灵动微电子六步换向(方波控制)方案MM32BIN560C] 六步换向实现和规律
  • PostgreSQL诊断系列(2/6):锁问题排查全攻略——揪出“阻塞元凶”
  • RK3568 Linux驱动学习——pinctrl和gpio子系统
  • onnx入门教程(四)——ONNX 模型的修改与调试
  • Day24: NumPy 奥德赛:用科学计算的魔法征服数据宇宙!
  • 32.Ansible平台搭建
  • 2024年09月 Python(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • NFC线圈设计计算
  • 力扣热题——前K个高频元素
  • 记一次Arrays.asList集合删除的错误
  • Java vs Kotlin 在实际开发中的主要区别与面试题总结
  • 太阳光模拟器在国防军工中的应用
  • k8s-容器化部署论坛和商城服务(小白的“升级打怪”成长之路)
  • K8s Pod驱逐机制详解与实战
  • SpringBoot防重放攻击的5种实现方案
  • 什么是数据库?现代数据库类型、示例与应用(2025)
  • 深入理解 iptables:Linux 防火墙从入门到精通
  • Vue3使用 DAG 图(AntV X6)
  • 2024年12月 Python(二级)真题解析#中国电子学会#全国青少年软件编程等级考试
  • Spring Boot 3.5 新特性
  • C++ namespace
  • 国内外大模型体验与评测:洞察智能时代的核心驱动力一、引言