本文主要介绍了LInux操作系统中进程信号的相关内容,具体包括信号的产生、种类、注册、注销、自定义处理、捕捉、阻塞等。
目录
信号概念
信号是一个软件中断,他只是一个信号,但是这个信号怎么处理,什么时候处理由进程决定,所以也称软中断
信号产生
(1)硬件产生信号
硬件产生信号举例:
ctrl+c:2号信号SIGINT
ctrl+z:20号信号SIGTSTP
ctrl+|:3号信号SIGQUIT
kill -l 查看全部信号
硬件产生信号执行动作:
Term:终止
Tgn:忽略
Core:终止并产生核心转储文件
Stop:暂停一个进程
Cont:如果进程暂停,则继续运行
(2)软件产生信号
kill函数
int kill(pid_t,int sig); 给pid进程发送sig信号
kill -[数字] [pid] 给进程发送对应数字信号
raise函数
int raise(int sig); 谁调用给谁发信号
信号种类
信号总共由62个:1-64中除去32、33
(1)非可靠信号(非实时信号)
1号信号——30号信号
(2)可靠信号(实时信号)
34号信号——64号信号
(3)区别
非可靠信号可能存在信号丢失的情况,可靠信号一定不会有信号丢失的情况
信号处理方式
操作系统对信号的处理方式(man 7 signal):term、core、cont、ign、stop
默认处理方式:SIG_DFL,操作系统当中已经定义好信号的处理方式
忽略处理方式:SIG_IGN,该信号为忽略处理(僵尸进程)
补充:僵尸进程产生的原因:当子进程先于父进程退出时,会发送给父进程一个SIGCHLD信号(17号信号),父进程接收到信号后做忽略处理,没有回收子进程的退出状态信息,导致子进程变成僵尸进程。
自定义处理方式:程序员可以更改信号的处理方式,定义一个函数,当进程收到该信号的时候,调用程序员自己写的函数
信号注册
概念:一个进程收到一个来自操作系统内核的信号,这个过程称为注册
注意:信号的注册和信号的注销是两个独立的过程
进程(struct task_struct)
信号注册位图:struct sigpending pending(注册位图,或未决位图)
sigqueue队列:
补充:1、sig是一个数组,数组的元素是long
2、long类型在64位的Linux操作系统中占有8字节
3、数组并不是按照long类型来使用的
4、数组的使用方式是按照比特位来使用的
信号注册:信号对应的比特位修改成为1,添加sigqueue节点到sigqueue队列当中
区别:
非可靠信号(非实时信号)
第一次注册
修改sig位图(0-1),添加sigqueue节点
第二次注册(同一个信号,在前一个还未被处理的情况下,再次注册)
修改sig位图(1-1),不添加sigqueue节点
可靠信号(实时信号)
第一次注册
修改sig位图(0-1),添加sigqueue节点
第二次注册
修改sig位图(1-1),再次添加sigqueue节点
信号注销
非可靠信号(非实时信号)
1、将信号对应的sig位图当中的比特位置为0
2、将对应的信号的sigqueue节点进行出队操作
可靠信号(实时信号)
1、将对应的信号的sigqueue节点进行出队操作
2、判断sigqyeye队列当中还有相同信号的sigqueue节点吗
如果有:则比特位不变
如果没有:则比特位改变位0
信号自定义处理方式
自定义处理方式,就是让程序员自己定义某一个信号的处理方式
(1)函数一
sighandler_t signal(int signum,sighandler_t handler);
参数:
signum:信号值
sighandler_t:函数指针:
1、接收一个函数地址,没有返回值,参数类型为int
2、将信号的处理方式替换成为函数的指针保存的函数地址所对应的函数
handler:更改为哪一个函数处理,接收一个函数地址,函数指针
typedef void(*sihhandler_t)(int);
注意:
void sigcallback(int sig){ } signal(2,sigcallback);
当执行signal函数的时候,并不会调用sigcallback函数,而是将sigcallback函数的地址保存在函数指针当中
当进程收到自定义处理的信号之后,回调该函数
9号信号(强杀信号):是不能被自定义处理的
(2)函数二
int sigaction(int signum,const struct sigaction *act,struct sigaction *oldact);
参数:
signum:信号值
act:将信号的处理方式更改为act(输入型参数)
oldact:原来信号的处理方式(输出型参数)
信号捕捉流程
操作系统空间分为用户空间和内核空间,用户空间执行程序员自己写的代码,内核空间执行程序调用函数。
流程:
1、(用户空间)执行控制流程时因中断、异常或系统调用进入内核
2、(内核空间)内核执行系统调用
3、(内核空间)内核处理进程中传递的信号
4、(内核空间)信号处理方式为默认处理或忽略处理,在内核直接处理
(用户空间)信号处理方式为自定义处理,返回用户空间程序员自定义函数执行处理
5、(内核空间)由内核空间返回用户空间中主控制流程中断处继续执行
6、(用户空间)继续执行主控制流程
结论:
1、用户进程内核后才能处理信号
2、进程从内核返回到用户态的时候,一定会调用do_signal函数处理信号
2.1、进程没有收到信号,直接返回到用户空间
2.2、进程收到信号
2.2.1、默认处理,忽略处理:在OS中直接处理掉
2.2.2、自定义处理返回用户空间执行程序员自己写的程序
信号阻塞
信号阻塞:当进程收到信号后,由于阻塞,暂时不处理该信号
补充:信号的阻塞不会影响信号的注册
内核代码:
struct task_struct{ ... sigset_t blocked; //位图 ... }
信号阻塞时信号的处理方式:
进入内核返回之前,会调用do_signal函数处理信号
有信号要处理,则先判断该信号是否阻塞,没有阻塞则处理信号,信号阻塞则不处理信号
接口:
int sigprocmask(int how,const sigset_t *set,sigset_t *oldset);
参数:
how:想让sigprocmask做什么事情
SIG_BLOCK:设置某个信号为阻塞状态
SIG_UNBLOCK:设置某个信号为非阻塞状态
SIG_SETMASK:用第二个参数“set”,替换原来的阻塞位图
set:新设置的阻塞位图
oldset:原来老的阻塞位图
原理解析:
当how为SIG_BLOCK时,函数会根据set,计算新的阻塞位图,方式为:
block(new)=block(old) | set
当how为SIG_UNBLOCK时,函数会根据set,计算新的阻塞位图,方式为:
block(new)=block(old) & (~set);
当how为SIG_SETMASK时,函数会根据set,计算新的阻塞位图,方式为:
block(new)=set;
补充:
持续制作更新中, 创作不易求点赞,欢迎大家交流讨论!!!