本文介绍: 可以看到优化情况下对应捕捉到2号信号后,对应flag置为1,但是main中的循环还是结束,很明显while循环中的flaghandler中的flag已经不是同一个flag了,这就出现的二义性的问题了。在linux中,一个终端,一般会有一个bash每个登录,只允许一个进程前台进程,可以允许多个进程后台进程,大部分可执行文件执行起来是后台文件。4.一个进程信号产生,到信号被处理,就一定会有时间窗口,所以进程还有具有保存信号的能力。所以在是有信号捕获进程退出时候,最好用等待方式来进行退出
对信号和进程之间理解
1.进程必须具备识别信号的功能,即使信号没有产生,也具备这种功能
2.进程即使没有收到信号,也知道这些信号怎么处理。
3.当进程收到一个信号的时候可能并不会立即处理这个信号。
4.一个进程在信号产生,到信号被处理,就一定会有时间窗口,所以进程还有具有保存信号的能力
看一下所有的信号列表kill -l):
代码演示信号的作用
演示前先认识一个接口singal
可以自定义捕捉动作
对应运行之后:
一次捕捉我都按了一次ctrl +c
这次是在另一个窗口输入kill -2 指令,也捕获到了。
ctrl+c本质是被进程解释收到了2号信号。
而在捕捉1-34号之间所有信号的时候
只有9和19两个信号不能被捕捉。
1.ctrl+c为什么能够杀掉前台进程呢?
首先需要明白什么前台进程,什么后台进程:
linux中,一个终端,一般会有一个bash每个登录,只允许一个进程是前台进程,可以允许多个进程是后台进程,大部分可执行文件执行起来是后台文件
可以看到要转成前台文件在后面加&
对应这里ctrl+c可以终止进程。
其实就是因为:
ctrl+c的本质是被进程解释收到了2号信号,而键盘输入首先是被前台进程收到的。
2.键盘数据如何输入给内核的,ctrl+c如何变成信号的?
对应键盘中输入的数据直接给到os内核缓冲区,对应os就会判断输入的是数据还是
控制,如果是控制ctrl +c),那就直接将其转换为2信号发送给进程。
而在硬件的层面对应会产生硬件中断中断正在进行工作的硬件。
我们学习的信号,就是软件方式,对进程模拟硬件中断
2.信号的产生:
1.键盘组合键:
            对应kill中的2号信号;
            对应kill中的3号信号,验证一下:
对应可以使用kill数字 + 进程来对进程发送信号。
kill接口
对应使用
对应就简单实现了一个自己kill命令
对应kill之后直接停止了。
raise接口:(用的少)
对应的使用
结果
对应只对自己所在的进程起作用参数里写是几对应就发送四几个信号
对应在信号中有一个叫SIGABRT:
对应代码验证
结果
功能就是直接发送六号信号。
信号一定是先发给os的,os是进程的管理者
4.异常
对应代码演示
1.除0错误
对应结果
对应在错误信号里寻找:
捕获信号之后:
结果
2.野指针问题
对应的代码
对因的结果
在信号中找:
那么为什么异常会给进程发信号呢?
对应产生异常之后会异常有限发送给操作系统操作系统会向解析信号并发送给进程。
这种除零保存值发生在cpu运算器在中的,而CPU也属于硬件,也要被操作系统管理,所以信号还是发送给操作系统的:
alarm接口:
对应的参数是秒数:
对应的结果
可以看到捕获到了。
对应剩余时间的概念
结果
对应kill一次之后会显示一次alarm的剩余时间。
对应信号的产生就有这5中方法
看一下Core Dump:
 
对应的是一个进程停止信号:
这是将进程等待的一个图,其中被信号所杀是会有异味是core dump 标志。
通过进程等待的代码来看一下对应的标志:
对应的结果:
kill -2 的结果:
对应的core dump没有改变。
再看对应kill -8 的结果:
对应的curedump为1了。
方便事后调试:(先运行运行之后再调试)
makefile中加上-g选项
对应debug下多出来了一个很大对文件core.15894
打开系统的corecomp功能,
一旦进程异常退出,OS会将进程在内存中的运行信息
给转存到进程的当前目录中,形成core.pid信息
所谓的核心转储(core dump).
3.信号的发送
信号只有31个,难道是巧合吗?
不是,信  号是存在位图中的。
所以os也是进程的管理者,只有它才有资格修改进程的task_struct内部属性
4.信号的保存
首先来谈信号的存储
对应如图所示
对应除实体信号之外只有31个信号,存储一张位图中:
对应每一个信号都要有一种对应的处理方法,对应存在函数指针数组
就是上图中的第三个数组。
一次简绍
1.block:
表示阻塞的标志位,对应在这个位图中被标志位1后就会在产生该信号是无法正常递达,因为被阻塞了。
注意:
阻塞忽略不同
阻塞
只要一个信号被阻塞就不会被递达。
忽略
信号的递达是成功的,只是拒绝的接受。
2.pending:
这里状态是信号从产生到递达之间的状态,称为信号未决。
只要有信号的出现就会在pending位图里面置1.
3.handler:对每个信号的处理方法,上图只有两个剩下的需要自定义
对应的图解:
信号集操作函数
sigpending:
对应的代码实现:
结果:
代码主要分为三步:
1.对信号2屏蔽
3.解除阻塞
对应使用oset
如果将所有信号都屏蔽了呢?
对应发现9和19号无论如何都不能被屏蔽
5.信号的捕捉处理:
什么时候处理的?
当进程从内核返回用户态的时候,进行信号的检测和处理。
什么用户态和内核态?
在CPU中有一个叫ecs寄存器里面存有两位二进制数,对应的1就是用户态,3就是内核态。
基于时钟中断的一个死循环
下来看一张图,对应用户捕捉代码进行的转换:
我们再来认识一个信号捕捉的函数接口:
有三个参数:
第一个signal函数的参数一样,其他两个参数都是对应的结构指针我们来看一下这个结构体:
我们只看里面第一个属性和第三个属性,其他的不看
代码来验证这两个属性
对应的结果:
如果想屏蔽更多信号,就要用到第三个属性
对应的代码:
结果:
必须要打印说出来pending表才利于观察。
对应的结论:
某个信号的处理函数调用
,
内核自动当前信号加入进程的信号屏蔽
,
信号处理函数返回自动恢复原来
的信号屏蔽
,
这样就保证了在处理某个信号时
,
如果这种信号再次产生
,
那么 它会被阻塞到当前处理结束为止
重入函数
对应的状况:
1.main
函数调用
insert
函数向一个链表
head
插入节点
node1,
插入操作分为两步
,
刚做完第一步时候
2.因
为硬件中断使进程切换内核
,
再次回用户态之前检查到有信号待处理
,
于是切换
sighandler

数。
4.sighandler
返回内核
,
再次回到用户态就从
main
函数调用
insert
数中继续 往下执行
,
先前做第一步
之后被打断
,
现在继续做完第二步
5.结果是:
main
函数和
sighandler
先后 向链表中插入两个节点
,
最后
有一个节点真正插入链表中了。
如果一个函数被重复进入的情况下,出错了,就是不可冲入函数。
否则,就是可重入
不可重入函数的条件
1.调用malloc,new获胜者free
2.调用标准io库的。
先写一段代码
MAKEFILE:
结果:

可以看到优化情况下对应捕捉到2号信号后,对应的flag置为1,但是main中的循环还是结束,很明显while循环中的flaghandler中的flag已经不是同一个flag了,这就出现的二义性的问题了。
原因
可以看到在main数中flag是没有修改的,而!运算
一直在cpu计算这里编译器会对flag产生优化直接
flag待在寄存器中,方便CPU的计算
我们不想要这种优化时候,同时为了避免二义性,就可以加关键字volatile
来避免这种优化:
SIGCHLD信号:
子进程在终止时会给父进程发送SIGCHLD信号:
属于第17号信号。
代码验证:
结果:
可以看到被捕捉到了。
这样我们就可以实现异步控制等待子进程:
对应的结果:
其实该信号的默认处理动作忽略,这样可以使父进程专心做自己工作
但是这样也有弊端,我发看到子进程的退出信息
等待的好处:
1.获取子进程的退出状态,释放子进程的僵尸
2.虽然不知道父子谁先运行,但父进程一定是最后退出
所以在是有信号捕获子进程退出的时候,最好用等待的方式来进行退出。

原文地址:https://blog.csdn.net/m0_61497245/article/details/134802135

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任

如若转载,请注明出处:http://www.7code.cn/show_44206.html

如若内容造成侵权/违法违规/事实不符,请联系代码007邮箱suwngjj01@126.com进行投诉反馈,一经查实,立即删除

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注