信号是软件中断。一个信号就是一个消息,它通知进程系统中发生了一个某种类型的事件。下面首先列举了一些比较重要的信号及其对应的事件,并且我们将在后面重点介绍:
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。