1、信号的定义
信号就是一个软件中断,通知进程发生了某个事件,打断进程的当前操作,去处理这个事件。
信号是多种多样的,并且一个信号对应一个事件,这样才能收到一个信号后,知道到底是一个什么事件,应该如何处理。(必须要保证能识别这个信号)
信号的种类:62种
1 ~ 31是非可靠信号(事件可能会丢失)
34~64是可靠信号。(事件不会丢失)
在xshell中用命令 kill -l 查看
具体的信号指令:
2、生命周期
产生、进程中的注册、进程中的注销、捕捉处理
(1)信号的产生
硬件产生:通过硬件产生的信号
例如:ctrl + c 、ctrl + | (退出信号)、ctrl + z (停止一个进程,会产生僵尸进程)
软件产生:
kill命令 :kill -signum pid
kill +进程id:杀死一个进程(当进程处于停止状态时,并不会杀死进程)
强制杀死进程:kill -9 (操作9号命令,来杀死进程,无论是什么状态都可以杀死)
kill的系统调用接口:
int kill(pid_t pid, int signum);//给任意进程或进程组发送任意一个信号
常用系统调用接口:
int raise(int signum);// 给自己发送指定信号(库函数)
void abort();//给当前调用进程发送一个sigabort(终止)信号
int alarm(int seconds);//通常称为定时器,是指在n秒之后发送一个sigalarm信号
int sigqueue(pid_t pid, int sig, const union sigval value);//给任意进程发送任意信号,并且在发送信号的同时传输一个数据
core dumped:核心转储
指在程序异常退出时,保存程序的运行信息,便于事后调试
默认关闭状态
ulimit -a:查看当前用户的限制信息
ulimit -c:设置核心转储文件的最大大小
ulimit -c 1024 则最大大小为1M
应用流程
gdb ./main => core-file core.pid => 事后的命令调试
(2)信号在进程中的注册
在pcb中有一个未决信号集合,pending集合,信号的注册就是指在这个pending集合中标记对应信号数值的二进制位为1
1~31非可靠信号的注册:若信号还未注册则注册,添加一个sigqueue结点(即将对应的信号数值的二进制位置为1);若已经注册,则什么也不做,直接返回(导致与注册过信号相同的信号丢失)
34~64可靠信号的注册:每次注册信号,不管是否已经注册,都会添加一个sigqueue结点(信号信息)。每次添加sigqueue结点都会修改pending位图,而且会添加一个sigqueue结构体
(3)信号的注销
删除要处理的信号sigqueue结点
若信号为非可靠信号:则直接将位图置零(非可靠信号指只会注册一次)
若信号是可靠信号:则删除后,需要判断是否还有相同结点,没有的话才会将位图置零
(4)信号的捕捉处理
- 默认处理:SIG_DFL
- 忽略处理:SIG_IGN
- 自定义处理:void sigcb(int signum)
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);//修改信号的回调函数
signal函数
作用1:站在应用程序的角度,注册一个信号处理函数
作用2:忽略信号,设置信号默认处理 信号的安装和回复
参数解释
- signal是一个带signum和handler两个参数的函数,准备捕捉或屏蔽的信号由参数signum给出,接收到指定信号时将要调用的函数有handler给出
- handler这个函数必须有一个int类型的参数(即接收到的信号代码),它本身的类型是void
- handler也可以是下面两个特殊值:① SIG_IGN 忽略该信号 ② SIG_DFL 恢复默认行为
sigaction(int signum, struct sigaction *new, struct sigaction *old);
//修改信号的处理动作为newact,原来的动作使用oldact保存
自定义信号的捕捉流程:
信号的处理是在程序运行从内核态切换到用户态之前,默认/忽略直接在内核中完成处理,而用户自定义信号处理方式,则需要返回用户态执行回调函数,完成后返回内核态,最终没有信号处理了,再返回程序主控流程。
进程如何从用户态切换到内核态:中断、异常、系统调用接口(例如a/0会触发异常,write是系统调用接口)