Linux 进程信号:信号的概念、生命周期、产生流程、阻塞
信号的概念
信号
信号是一个软中断。操作系统通过信号通知某个进程发生了某件事件,然后中断这个进程当前操作,让它优先去处理这个事件。
我们在linux下常用的kill命令就是通过向进程发送一个信号来使进程中断,我们可以通过kill -l来查看信号的种类
信号的种类
可以看到32和33号信号是不存在的,并且1-31是有具体名称的,而34号及之后的都是以SIGTMIN+数字命名。
信号种类:62种
不可靠信号:1-31号,从unix借鉴而来,每个都有对于的系统事件,不可靠是因为可能会丢失信号导致事件丢失
可靠信号:34-64号,后期扩充的,因为没有具体对应的事件,所以命名比较草率,不会丢失信号。
信号的生命周期
生命周期:产生信号->在进程中注册信号->在进程中注销进行->处理信号
信号的产生:
-
通过终端按键产生信号
例如常用的ctrl + z、ctrl + c、ctrl + \就是分别产生了SIGTSTP、SIGINT、SIGQUIT信号。 -
通过调用系统函数向进程发送信号
例如kill -x函数,x就是对应的信号的序号,如果不知名则发送15号信号SIGTERM。kill杀死进程的原理就是通过发送一个信号,让这个进程中断并去处理这个信号,然而这个信号的处理结果就是让这个进程退出。
3.通过软件异常产生信号
例如上一篇说过管道如果读端全部关闭,而写端没关闭时就会发送一个SIGPIPE的信号
4.通过硬件异常产生信号
例如当运算中以0为除数,则CPU的运算单元会检测到除0异常,并发送SIGFPE信号
信号的注册
信号注册的流程主要是修改pcb中的pending位图并向pcb中的sigqueue链表中添加新的节点,但根据信号的种类不同操作也不同。
不可靠信号的注册:
首先查看pending位图该信号的标志位是否为0,如果为0则将标志位修改为1,并向sigqueue链表添加新的节点。如果为1则说明该信号已经注册过,则忽略此次事件,什么都不做,也正是因为这样会导致事件的丢失,才被称为不可靠信号。
可靠信号的注册:
可靠信号注册时则不管该信号是否注册过,都会往sigqueue链表中添加新的节点并修改位图,这样就保证了每一个发送的事件都会被处理,这也是被称为可靠的原因。
信号的注销
为了保证每一种信号只被处理一次,所以需要先注销再处理。
注销就是消除这个信号存在的痕迹,即修改位图,删除sigqueue中的节点。
不可靠信号的注销:
因为不可靠信号只注册了一次,只需要删除sigqueue中的节点,然后所以将位图对应的标志位置零。
可靠信号的注销:
因为可靠信号注册了多次,添加了多个节点,所以需要删除该信号添加的所有相同节点,才将位图对应的标志位置零。
信号的处理
因为信号是操作系统发给进程来通知某个事件的到来,所以对信号的处理也就是对事件的处理。
信号的处理方式:
-
默认处理方式: 就是操作系统为每一种信号准备的对应的处理方式
-
忽略处理方式: 和名字一样,忽略,什么都不做
-
自定义处理方式: 我们可以自己写一个回调函数来替换原来的处理方法,完成我们想要对这个信号的处理方式。
信号的捕捉流程
- 在主函数时因为异常或者中断或者系统调用进入内核态。
- 处理完异常后开始处理信号
- 调用用户自定义的回调函数,返回用户态
- 回调函数执行完毕,通过系统调用sigreturn返回内核态
- 信号处理完毕,调用sys_sigreturn()返回到一开始主函数被中断的地方,继续执行下面的语句。
接口:
sighandler_t signal(int signum, sighandler_t handler);
信号
typedef void (*sighandler_t)(int);
//回调函数,这是一个函数指针,我们通过编写自己的回调函数,然后signal通过函数指针来找到这个函数,替换到原来的操作
signum:信号的序号
handler:处理方式
头文件:#include <signal.h>
返回值:返回一个函数指针,也就是第二个参数
信号的阻塞
信号的阻塞并不是不注册一个信号,而是注册后暂时先不对它进行处理,在block位图中修改标志位,将其阻塞,直到进程解除对该信号的阻塞的时候再处理这个信号
同时,有两个信号比较特殊,9号信号SIGKILL和19号信号SIGSTOP,这两个信号不可被阻塞,不可被忽略,不可被自定义处理。
接口: