信号


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

  • 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 的信号。
  • 如果 handlerSIG_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。 和一些与信号有关的系统函数: killpause