目录
1.默认处理方式:(SIG_DEF),操作系统当中已经定义信号的处理方式了
2.忽略处理方式:SIG_IGN,该信号为忽略处理(僵尸进程产生的原因)
3.自定义处理方式:程序员可以更改信号的处理方式,定义一个函数,当进程收到该信号的时候,调用程序员自己写的函数。
2.信号的处理时机:当从内核态切换回用户态的时候,会调用do_signal函数处理信号
一、信号的概念
信号是一个软中断,只是告诉有这样一个信号,但是这个信号具体怎么处理,什么时候处理由进程决定的,所以是软中断。
举个不恰当的例子:看到了红灯你可以选择走也可以选择不走,绿灯你也可以选则走,也可以选择不走。
二、信号的产生
1.硬件产生(键盘敲)
例如:
ctrl + c:2号信号 SIGINT(退出一个进程)
ctr + z:20号信号 SIGTSTP(终止一个进程),可以通过ps aus | grep “进程名”查看(运行着的进程)进程状态
ctr + | :3号信号SIGQUIT(退出一个进程)
除此之外我们还可以通过kill命令向进程发送信号,格式:kill [信号值] [pid]
2.软件产生(函数代码)
kill函数:注:以后的函数都使用图片表示,方便查看
raise函数:
3.扩展知识
查看崩溃程序收到的信号的步骤:
>>1.我们先将生成coredump文件的大小设置为unlimited
>>2.我们在生成可执行程序的命令后加上-g选项,生成dubeg文件
>>3.使用gdb调试生成的coredump文件
常见的几种进程崩溃的原因:
1.解引用空指针(11号信号)
2.内存访问越界(11号信号)
3.除0(8号信号)
4.double free(6号信号)
三、信号的种类
kill -l 可以罗列信号,总共有62个信号:
信号分为两种:
非实时信号(非可靠信号):信号可能会丢失,1~31号
实时信号(可靠信号):信号不会丢失33~64号
四、信号的处理方式
操作系统对信号的处理方式:(man 7 signal)
1.默认处理方式:(SIG_DEF),操作系统当中已经定义信号的处理方式了
》》2号信号---终止进程
》》11号信号---终止进程,并且产生核心转储文件
2.忽略处理方式:SIG_IGN,该信号为忽略处理(僵尸进程产生的原因)
进程收到忽略处理的信号后,是不进行处理的。
例如SIGCHLD信号,子进程先于父进程退出,向父进程发送SIGCHLD,而父进程接收到这个信号后是忽略处理的,导致了父进程并没有回收子进程的退出状态信息,从而子进程变成了僵尸进程。
3.自定义处理方式:程序员可以更改信号的处理方式,定义一个函数,当进程收到该信号的时候,调用程序员自己写的函数。
五、信号的注册
基础概念
一个进程收到一个信号,这个过程称之为注册,信号的注册和注销是两个独立的过程。
信号的注册:
位图更改为1,添加sigqueue节点到sigqueue队列
1.信号注册的时候会将信号对应的比特位从0修改为1,表示当前进程收到了信号。
2.在sigqueue队列当中添加一个sigqueue节点,队列在操作系统内核当中本质上就是一个双向链表(先进先出的特性)
实时信号和非实时信号注册的区别:
非实时信号:多次注册同一个信号的时候,只会修改一次位图(信号没有被注销的前提下),而且只会添加一个sigqueue节点
实时信号:多次注册的时候同样只会修改一次位图,但是注册几次就会添加几次的sigqueue节点。
信号注册的时候
六、信号的注销
非可靠信号
1.将信号对戏那个的sig位图当中的比特位设置为0
2.将对应信号的sigqueue节点进行出队操作
可靠信号:
1.将对应信号的sigqueue节点进行出队操作
2.判断sigqueue队列当中是否还有相同的sigqueue节点,有的话就不修改位图,否则,将信号对戏那个的sig位图当中的比特位设置为0
七、信号的自定义处理方式
1.信号的自定义处理方式,就是让程序员自己定义某一个信号的处理方式
2.对应的自定义处理的函数
signal函数:
sigaction函数:
二者进行一个比较:
signal函数修改的其实是struct sigaction结构体中的(*sa_handler)(int)这个函数指针的指向为我们的自定义方式---》修改的是结构体的一个成员
而sigaction函数修改的是整个struct sigaction结构体。---》修改的是整个结构体
代码测试:
signal函数与sigaction函数原理图:
八、信号的捕捉流程
1.流程图:
2.信号的处理时机:当从内核态切换回用户态的时候,会调用do_signal函数处理信号
有:就处理信号(信号的处理方式(默认、忽略、自定义))
分为三个步骤:1.判断当前信号是否被阻塞
2.信号的注销过程
3.调用信号的处理方式函数
没有:就直接返回用户态
3.处理信号的时候,不同的处理方式
默认,忽略:直接在内核就处理结束
自定义处理:调用程序员自定义的处理函数进行处理
- 执行用户自定义的处理函数(用户空间)
- 调用sigreturn()再次回到操作系统内核(内核空间)
- 再次调用会调用do_signal函数处理信号
- 调用sys_sigreturn函数回到用户空间,继续执行代码
4.常见的进入到内核的方式:
调用系统调用函数,内存访问越界,访问空指针,调用库函数
九、信号的阻塞
1.概念
信号的注册是信号的注册,信号的阻塞是信号的阻塞。信号的阻塞并不会干扰信号的注册,而是说进程收到这个信号之后,由于阻塞,暂时不处理该信号。阻塞并不是说不处理该信号,而是等该信号不阻塞了之后,再进行处理。
2.内核代码:
3.加上信号阻塞之后,理解信号的处理:
- 进入内核,返回之前,会调用do_signal函数处理信号
- 有信号要处理,则先判断该信号是否阻塞,如果没阻塞,则处理信号,如果阻塞,则不处理
4.接口
原理解析:
设置阻塞:
新的位图与旧的位图相或
设置非阻塞:
1.将新的位图按位取反
2.与旧的位图相与
利用阻塞性质验证可靠信号和非可靠信号在sigqueue中的注册方式的不同:
十、其他扩展内容
1.父子进程+进程等待+自定义处理方式
2.volatile关键字
作用:保存内存可见性
每次CPU要计算的数据都是从内存当中获取,拒绝编译时的优化方案(从寄存器当中获取)
gcc/g++的编译器选项“-00,-01,-02,-03”,优化时级别越来越高。(理解优化级别越高,程序执行的越快)