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.






猜你喜欢

转载自blog.csdn.net/qiana_/article/details/80367272