大家应该都了解当我们在Linux下面运行程序时,如果程序跑飞了,那我们可以通过Ctrl-C来终止掉跑飞的程序,其实,当我们按下Ctrl-C时,涉及到了信号的相关处理工作,所以,今天我们就来谈一谈Ctrl-C被后的信号处理吧。
什么是信号
信号其实是一种软件中断,它为程序提供了一种处理异步事件的方法,所谓的异步事件就是事件的发生在任何事件都有可能,很多重要的程序都需要对信号进行处理。在Linux中一共有多少信号呢?我们可以通过在shell下运行kill -l指令,来查看Linux下的信号数目和他们对应的编号。如下图:
其中,我们把编号为1~31的信号称为普通信号,把34~64的信号称为实时信号。所有的信号名在头文件signal.h,中都被定义为正整数常量,即他们的信号编号。
信号的产生方式
在Linux下,信号可以通过一下几种方式来产生:
- 当用户按某些终端键时,引发终端产生的信号,例如我们前面提到的Ctrl-C按键对应的是SIGINT信号,Ctrl-\按键对应的是SIGQUIT信号。
- 硬件异常产生信号,例如除数为0、无效的内存引用对应的SIGSEGV信号,硬件异常产生信号不同,一旦硬件异常产生,那么他会一直存在,直到程序被终止位置,所以处理硬件异常信号一般都采用终止程序的方法。
- 进程调用kill(系统调用)函数可将任意信号发送给另一个进程或者进程组;(但是这条是有限制的,例如:要求接收信号和发送信号的进程的所有者必须相同,或者发送信号的所有者必须是超级用户)。
- 进程可以通过在shell下运行kill指令来对某个进程发出信号,kill指令是kill系统调用的一个接口。
- 当检测到某些软件条件时,也会产生信号。
信号的处理方式
有了信号的产生,当然也就有信号的处理,在Linux中,对信号的处理有下面的几种方式:
1. 忽略此信号,大多数信号都可以采用这种方式进行处理,除了9) SIGKILL 和19) SIGSTOP 信号。因为这两种信号都直接向内核提供了进程终止和停止的可靠办法。(SIGKILL),还有硬件异常信号我们最好不要忽略,因为硬件异常一旦产生如果不进行处理就会一直存在。
2. 捕捉信号。 进程要通知内核在某种信号产生时,需要调用一个用户函数,在用户函数中,用户可以自己定义信号处理的方式,在Linux下我们不能捕捉SIGKILL信号和SIGSTOP信号。
3. 执行系统的默认动作。
终止+core
在系统默认动作中,有一种动作叫做“终止+core”,它表示在进程当前工作目录中的core文件中复制了该进程当前的内存映像,(该文件名为:core.进程pid号),很多信号都使用了这种处理方式,例如SIGFPE信号。当产生了内存映像文件,我们可以在gdb下面进行调试。如下图:
其中ulimit -c + number是指定core文件的大小上限,
ulimit -a是查看当前系统中的各个资源上限。
相关信号处理函数
- signal函数(指定信号的处理方式)
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
signum:参数为信号名,或者信号编号。
handler:为指向返回值为void参数为int的函数指针,或者是SIG_IGN或SIG_DFL宏定义。
在signal.h头文件中,上面的常量被定义如下
#define SIG_ERR (void(*)()) -1
#define SIG_DFL (void(*)()) 0
#define SIG_IGN (void(*)()) 1
其中,SIG_DFL指定信号处理的方式为默认方式,SIG_IGN指定信号处理的方式为忽略。
- alarm函数
#include <unistd.h>
unsigned int alarm(unsigned