Linux 信号signal
1. 信号的基本概念
联系现实生活中的信号,我们可以理解为:Linux中的signal其实就是一种标志,操作系统知道这个标志代表着什么含义,以及知道遇见这个标志它应该采取怎样的动作。(如红灯信号,你知道红灯信号代表着不能通过马路,而你采取的动作就是等待)
1.1 信号的分类:
使用kill -l : 可以查看系统定义的信号列表。
总共有62种信号。1-31号信号称为普通信号,34-64号信号称为实时信号。
每个信号都有一个编号和一个宏定义的名称,这些宏定义可以在/usr/include/signal.h中找到。
以1号信号为例,它的宏定义为:#define SIGHUP 1
1.2 信号的常见处理方式
- 忽略信号
- 执行默认的信号处理动作
-
捕捉信号。提供一个信号处理函数,要求内核在处理信号时切换至用户态执行这个处理函数
2. 产生信号
信号通过以下三种方式产生,下面分别进行介绍:
2.1 通过终端按键产生信号
用户在终端按下某些键的时候,终端驱动程序会发送信号给前台进程。比如:按下Ctrl-c会产生SIGINT信号,SIGINT信号的默认处理动作是终止进程;按下Ctrl-\ 会产生SIGQUIT信号,SIGQUIT的默认处理动作是终止进程并且Core Dump ; 按下Ctrl-z 会产生SIGSTP信号,SIGSTP信号可以使前台进程停止。
什么是 Core Dump?
当一个进程要异常终止时,可以选择把进程的用户空间中的内存数据全部保存到磁盘上,文件名通常是core,这个过程就叫做core dump。
进程异常终止通常是因为有bug,事后可以用调试器检查core文件以查清错误原因,这就叫做Post-mortem Debug(事后调试)。
core文件的大小?
一个进程允许产生多大的core文件取决于进程的Resource Limit(该信息通常保存在PCB中)。默认是不允许产生core文件的,因为core文件中可能包含用户密码等敏感信息,不安全。在开发调试阶段可以用ulimit 命令来改变这个限制,使之允许产生core文件。
使用ulimit -a 来查看系统中某些文件的大小。
2.2 调用系统函数向进程发信号
- kill命令。kill命令是调用kill函数实现的,kill函数可以给一个指定的进程发送指定的信号。例如:kill -2 4980 给4980号进程发送一个2号信号(即SIGINT信号)
- raise函数可以给当前进程发送指定的信号(自己给自己发信号)
- abort函数可以使当前进程接受到信号而异常终止
函数说明:
#include <signal.h>
int kill(pid_t pid, int signo);
int raise(int signo);
参数:
pid :进程的进程ID
signo :信号的编号
返回值:
成功返回0,错误返回 -1
一个简单的例子:子进程循环5次后,休眠10秒,收到SIGINT信号,退出进程。
#include <stdlib.h>
void abort(void);
像exit函数一样,abort函数总会成功,没有返回值。
2.3 由软件条件产生信号
通过alarm函数:调用alarm函数可以设定一个闹钟,也就是告诉内核在seconds秒之后给进程发SIGALRM信号,该信号的默认动作是终止当前进程。
#include <unistd.h>
unsigned int alarm (unsigned int seconds);
这个函数的返回值是0或者是以前设定的闹钟时间还余下的秒数。返回0,表示这个闹钟成功的响了。
为什么会返回以前设定的闹钟时间还余下的秒数呢?可以这样理解:
小明要睡觉,他定了一个60分钟后响的闹钟,结果他在45分钟的时候就醒了,有两种情况:
- 小明不想睡了,他把这个闹钟关了,于是alarm(0) 让seconds为0,表示取消以前设定的闹钟,函数的返回值是以前设定的闹钟时间还余下的秒数。
- 小明还想再睡20分钟,于是他重新设定闹钟为20分钟后响,则以前的闹钟返回的就是以前设定的时间还余下的秒数。
一个闹钟的使用例子:1秒之内,count一直计数,1秒后alarm函数给进程发送SIGALRM信号,终止当前进程。
3. 阻塞信号
3.1 信号的几种状态
信号递达(delivery):实际执行信号的处理动作
信号未决(pending):信号从产生到递达之间的状态
进程可以选择阻塞某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达动作。
注意:阻塞和忽略的区别。只要信号被阻塞,那么就不会递达;而忽略是信号在递达后采取的动作。
3.2 信号在内核中的表示
怎么知道操作系统是否给进程发送了信号?
用一个位图来表示31种信号,每一个比特位的位置决定了信号的编号,比特位的内容(0或1)表示收到还是没有收到信号。
例如:第五个比特位为1,表示进程收到了5号信号。PCB中必定包含一个 int signal 字段(位图)。
因此站在操作系统的角度上,操作系统给进程发送了一个信号,就是操作系统修改了该进程PCB中的signal位图的第五个比特位,将0改为1,就说明发送了一个信号。
怎么理解 kill -9 7890 命令?
用kill命令给进程号为7890的进程发送9号信号(SIGKILL)。kill命令是操作系统或shell提供的,底层会调用kill系统调用函数。
kill -9 7890 是操作系统找到7890号进程,把这个进程的PCB中的signal位图的第9个比特位由0置为1.
上一篇: 微信公众号获取code 和用户信息