信号是软件中断。一个信号就是一个消息,它通知进程系统中发生了一个某种类型的事件。下面首先列举了一些比较重要的信号及其对应的事件,并且我们将在后面重点介绍:
SIGKILL
杀死程序。SIGCHLD
一个子进程停止或者终止SIGFPE
浮点异常
每种信号类型都对应与某种系统事件。比如,如果一个进程试图除以0,那么内核就发送一个 SIGFPE
信号。如果当进程在前台运行时,你键入 ctrl-c
那么内核就会发送一个 SIGINT
信号给这个前台程序。 并且,一个进程可以通过向另一个进程发送一个 SIGKILL
信号强制终止它。当一个子进程停止或终止时,内核会发送一个 SIGCHLD
信号。
信号处理
当产生一个信号时,可以有三中不同的处理方式。
1.忽略此信号。
2.捕捉信号。 为了做到这一点,要通知进程在某种系统信号发生时调用一个 信号处理函数
。例如,如果捕获到 SIGCHLD
信号,则表示一个子进程已经终止,所以此信号的 处理函数
可以 无阻塞
地调用 waitpid
以回收该子进程的状态信息。
3.执行系统默认动作。
上面三者的关系,当我们没有注册 信号处理函数
时,进程会执行默认行为。(有一些默认行为是忽略此信号。)
1. SIGFPE
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void handler(int sig){
printf("handler is process id%d\n", getpid());
printf("%d exception occur", sig);
exit(0);
}
int main(){
printf("main function is process id %d ", getpid());
signal(SIGFPE, handler); //设置信号处理程序
int i = 5;
int zero = 0;
int j = i / zero;
printf("after...");
exit(0);
}
//output:
//process id 13652
//handler process id 13652
//8 exception
上面的程序展示了当发生 SIGFPE
信号时,进程通过调用其 信号处理程序
来处理此异常。
首先,我们来解释 signal
函数:
#include <signal.h>
typedef void (*sighandler_t)(int):
sighandler_t signal(int signum, sighandler_t handler);
signal
函数可以通过下列三种方法之一来改变和信号 signum
相关连的行为:
- 如果 handler 是
SIG_IGN
,那么忽略类型为signum
的信号。 - 如果
handler
是SIG_DFL
,那么类型为signum
的信号行为恢复为默认行为。 - 否则,
handler
就是用户定义的函数的地址,这个函数就是信号处理程序(signal handler)
,只要进程接受到一个类型为signum
的信号,就会调用这个程序。
ok,回到我们上面的程序,从输出结果中可以看到,handler
程序也是由那个发生异常的进程调用的。
最后,有必要在解释一下 handler
函数的参数 int sig
,它是要处理的那个信号的序号。每个信号都有一个唯一标识的序号。在这里, SIGFPE
的序号为 8。
2.kill 函数
进程可通过 kill
函数发送信号给其他进程。
#include <signal.h>
int kill(pid_t pid, int sig);
下面实例展示了父进程 kill
通过给子进程发送 SIGKILL
来杀死子进程。
#include <signal.h>
#include <unitd.h>
int main(){
pid_t pid;
if((pid = fork()) == 0){
pause();
printf("control should never reach here! \n");
exit(0);
}
kill(pid, SIGKILL);
exit(0);
}
我们还是先来解释一下 pause
函数吧。
#include <unistd.h>
int pause(void);
pause
函数使调用进程挂起直至捕捉到一个信号。
SIGKILL
信号默认会终止进程,并且这种默认行为是不能被修改的。
总结:本文首先引入了信号的概念,并介绍了几种常见的信号: SIGKILL
, SIGFPE
。 和一些与信号有关的系统函数: kill
, pause
。