【lesson49】信号之信号产生

文章目录

  • 信号的理解
  • 信号产生

信号的理解

我们要从三个方面讲解信号
【lesson49】信号之信号产生_第1张图片
我们生活中的信号有哪些?
红绿灯
闹钟
转向灯
狼烟
这些都是生活中的信号

1.我们为什么认识这些信号?
怎样才算认识信号?
【lesson49】信号之信号产生_第2张图片
例如: 红灯 + 停、绿灯 + 行

2.我们在我们的脑中能够识别这些信号

3.如果某个特定信号没有产生,但是我们依旧知道应该如何处理这个信号

4.我们在收到这个信号的时候,可能不会立即处理这个信号
例如:你的快递到菜鸟驿站了,但是你可能有自己的事情,并不会第一时间去拿这个快递

5.信号本身在我们无法立即被处理的时候,但是一定要被临时记住

什么是Linux下的信号?
本质是一种通知机制,用户 or 通过发送一定的信号,通知进程某些事件已经发送,进程可以在后续进行处理。

结合进程产生信号结论
a.进程处理信号,必须具备信号“识别的能力”看到信号+处理动作

b.凭什么进程能够“识别信号”?
程序员在写代码的时候就已经解决了这个问题!

c.信号产生是随机的,进程可能正在忙自己的事情,所以信号的后续处理,可能不是立即处理的

d.进程会临时的记录下对应的信号,方便后续处理

e.在什么时候处理呢?合适的时候(之后再讲解怎样算合适的时候)

f.一般而言,信号的产生相对于进程而言是异步的

例子:
你在网上买了很多件商品,再等待不同商品快递的到来。但即便快递没有到来,你也知道快递来临时,你该怎么处理快递。也就是你能“识别快递”
当快递员到了你楼下,你也收到快递到来的通知,但是你正在打游戏,需5min之后才能去取快递。那么在在这5min之内,你并没有下去去取快递,但是你是知道有快递到来了。也就是取快递的行为并不是一定要立即执行,可以理解成“在合适的时候去取”
在收到通知,再到你拿到快递期间,是有一个时间窗口的,在这段时间,你并没有拿到快递,但是你知道有一个快递已经来了。本质上是你“记住了有一个快递要去取”当你时间合适,顺利拿到快递之后,就要开始处理快递了。而处理快递一般方式有三种:
1.执行默认动作(幸福的打开快递,使用商品)
2.执行自定义动作(快递是口红,你要送给你的女朋友)
3. 忽略快递(快递拿上来之后,扔掉床头,继续开一把游戏)快递到来的整个过程,对你来讲是异步的,你不能准确断定快递员什么时候给你打电话

信号产生

我们写个循环的代码
【lesson49】信号之信号产生_第3张图片
当代码运行的时候,我们只有Ctrl C才能让它停下来
【lesson49】信号之信号产生_第4张图片
其实Ctrl C的本质是发送2号信号
测试
修改代码:因为发送信号需要pid,所以我们修改一下代码,获取一下进程pid
【lesson49】信号之信号产生_第5张图片
创建两个窗口:
窗口一运行代码:
【lesson49】信号之信号产生_第6张图片

窗口二发送2号信号:

【lesson49】信号之信号产生_第7张图片
结果:
【lesson49】信号之信号产生_第8张图片
信号处理的常见方式:
a.默认(进程自带的)
b.忽略(信号处理的一种方式)
c.自定义(捕捉信号,执行程序员写好的动作)

常见信号:
【lesson49】信号之信号产生_第9张图片
1-31:普通信号
34-64:实时信号
因为操作系统大部分是分时操作系统,也就是信号产生后不一定立即被执行,分时操作系统的信号是1-31

实时操作系统信号产生后一定立即被执行,实时操作系统的信号是34-64
【lesson49】信号之信号产生_第10张图片
每个信号(普通信号)的含义:
【lesson49】信号之信号产生_第11张图片
【lesson49】信号之信号产生_第12张图片
【lesson49】信号之信号产生_第13张图片
1.如何理解组合建变成信号?
2.如何理解信号被进程保存?
3.如何理解信号发送的本质?

1的答案:键盘的工作方式是通过中断方式进行的,OS当然也能识别组合键,例如:Ctrl C

2的答案:进程必须具有保存信号相关的数据结构(位图,unsigned int)
【lesson49】信号之信号产生_第14张图片
那么该位图在哪
进程的PCB内部保存该位图字段

3的答案:因为信号位图是在task_struct中---->而task_struct内核数据结构----->由OS管理
信号发送的本质OS向目标进程写信号,OS直接修改进程PCB中的指定的位图结构,最后完成“发送”信号的进程。

组合键执行流程
4.OS解释组合键---->查找进程列表----->找到前台运行的进程----->OS写入对应的信号到进程内部的位图结构中!

信号捕捉初识
【lesson49】信号之信号产生_第15张图片
参数解释:
signum:要捕捉的信号
handler:回调函数,通过回调的方式,修改对应的信号捕捉方法

代码:
【lesson49】信号之信号产生_第16张图片
运行结果:
【lesson49】信号之信号产生_第17张图片
我们发现确实捕捉到了Ctrl C发送的2号信号,但是我们发现Ctrl C不能终止进程了。

如果我们不要自己写的回调函数呢?
【lesson49】信号之信号产生_第18张图片
运行结果:
【lesson49】信号之信号产生_第19张图片
我们发现进程直接退出

通过上面我们发现signal函数,仅仅是修改进程对于特定信号的后续处理动作,不是直接调用对应的处理动作。

如果后续没有任何SIGINT信号的产生,catchsing会不会被调用?永远也不会被调用!

核心转储
【lesson49】信号之信号产生_第20张图片
Core Dump
首先解释什么是Core Dump。当一个进程要异常终止时,可以选择把进程的用户空间内存数据全部保存到磁盘上,文件名通常是core,这叫做Core Dump。进程异常终止通常是因为有Bug,比如非法内存访问导致段错误,事后可以用调试器检查core文件以查清错误原因,这叫做Post-mortem Debug(事后调试)。一个进程允许产生多大的core文件取决于进程的Resource Limit(这个信息保存 在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit命令改变这个限制,允许产生core文件。 首先用ulimit命令改变Shell进程的Resource Limit,允许core文件最大为1024K: $ ulimit -c1024
【lesson49】信号之信号产生_第21张图片
开启核心转储:
【lesson49】信号之信号产生_第22张图片
核心转储的作用:当进程出现某种异常的时候,是否由OS将当前进程在内存中的相关核心数据,转存到磁盘中。
【lesson49】信号之信号产生_第23张图片
Term不会发生核心转储
Core会发生核心转储
代码:
【lesson49】信号之信号产生_第24张图片
运行结果:
【lesson49】信号之信号产生_第25张图片
我们可以看到进程发送了核心转储,退出信号为8,为Action:core,所以发生了核心转储。
【lesson49】信号之信号产生_第26张图片
发送核心转储就会创建core.id(进程id)文件
那么这个core.id有什么用呢?
【lesson49】信号之信号产生_第27张图片
在调式代码的时候,会自动定位到错误位置。

为什么生产环境一般要关闭core_dump(核心转储功能)?
因为成本高,一般服务器都是成百上千个集群一起的,如果出错,人力排查的时间或者其它成本就会很高。

通过系统调用发送信号:
【lesson49】信号之信号产生_第28张图片
参数解释:
pid:进程id
sig:要发送的信号
代码:
【lesson49】信号之信号产生_第29张图片
运行测试:
打开两个窗口,一个窗口运行进程,一个窗口执行我们的代码杀掉进程
【lesson49】信号之信号产生_第30张图片
其它接口:
【lesson49】信号之信号产生_第31张图片
raise:自己向自己发送信号
【lesson49】信号之信号产生_第32张图片
abort:自己向自己发送6号信号
abort的作用和exit一样,通常用来终止进程。

如何理解系统调用发送信号?
用户调用系统接口----->执行OS对应的系统调用代码----->OS提取参数,或者设置特定数值------>OS向目标进程写信号----->修改对应进程的信号标记位------>进程后续会处理信号------>执行对应的处理动作!

由软件条件产生信号
管道如果读端不光不读数据,而且还关闭了,写端一直在写,那么写就没有任何意义,OS会自动终止对应的写端进程,通过发送信号的方式。SIGPIPE(13号信号)大家自行去验证

alarm
调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给当前进程发SIGALRM信号, 该信号的默认处理动作是终止当前进程。
【lesson49】信号之信号产生_第33张图片
代码:【lesson49】信号之信号产生_第34张图片
运行:
【lesson49】信号之信号产生_第35张图片
我们看到count加到快2万的时候就停止了。
但是我们发现设了一个闹钟,这个闹钟一旦触发,就自动移除了。并不能连续定时。
那么该如何解决呢?在回调函数中再设计alarm
代码:
【lesson49】信号之信号产生_第36张图片
运行结果:
【lesson49】信号之信号产生_第37张图片
之前我们看到count就加到2万左右,但是CPU的计算就这么低吗
1.因为我们一直cout,IO的效率非常低
2.我们的服务器在远端,所以还要再带上网络所用的时时间

测试计算机算力(一秒内):
代码:
【lesson49】信号之信号产生_第38张图片
运行结果:
【lesson49】信号之信号产生_第39张图片
如何理解软件条件给进程发送信号?
a.OS先识别到某种软件条件触发或者不满足
b.OS构建信号,发送给指定的进程

硬件异常产生信号
代码:
【lesson49】信号之信号产生_第40张图片
运行结果:
【lesson49】信号之信号产生_第41张图片
为什么除0错误后会一直循环呢?
1.进行计算的是CPU这个硬件

2.CPU内部是有寄存器的,状态寄存器有相对应的状态标记位,其中有溢出标记位,OS会自动进行计算完毕后检测,如果溢出标记位是1,OS里面识别到有溢出问题,立即只要找到当前谁在运行,直接提取PID,OS完成信号发送的过程,进程会在合适的时候,进程处理

3.一旦出现硬件异常,进程一定会退出吗
不一定,一般默认是退出,但是我们即便不退出也做不了什么!

4.为什么会死循环?
因为寄存器中的异常一直没有被解决!
【lesson49】信号之信号产生_第42张图片
11号信号:是段错误主要用来解决----->野指针问题、越界问题
代码:
【lesson49】信号之信号产生_第43张图片
运行结果:
【lesson49】信号之信号产生_第44张图片
那么为什么会循环报错呢?
1.问题都必须通过地址找到目标位置。
2.我们语言上的地址全部都是虚拟地址。
3.将虚拟地址转化成物理地址
4.页表 + MMU
野指针,越界问题----->一定是非法地址------->MMU转化的时候,一定会报错

所有的信号,有它的来源,但最终全部都是别OS识别、解释、并发送的!

总结思考一下
上面所说的所有信号产生,最终都要有OS来进行执行,为什么?OS是进程的管理者
信号的处理是否是立即处理的?在合适的时候
信号如果不是被立即处理,那么信号是否需要暂时被进程记录下来?记录在哪里最合适呢?
一个进程在没有收到信号的时候,能否能知道,自己应该对合法信号作何处理呢?
如何理解OS向进程发送信号?能否描述一下完整的发送处理过程?

你可能感兴趣的:(linux,Linux,x信号,C++)