信号捕捉与处理

信号是一种在事件发生时对进程的通知机制,信号来源大致有硬件异常、键入能产生信号的终端字符和软件事件。信号产生后,会稍后被传递给进程,而进程会采取某些措施来响应信号,在产生和响应期间,信号处于等待状态。

进程响应信号的方式有3种:忽略信号、执行信号处理函数、执行默认动作。

每种信号都有值为正的编号,可用kill -l命令查看信号列表,用kill -signo pid命令则可以向PID为pid的进程发送编号为signo的信号。

signal和sigaction函数都可用来改变信号处理,其中signal的行为在不同的unix实现间存在差异,可移植性不好,因此sigaction是建立信号处理器的首选API,实际上有些signal被实现为基于sigaction的glibc库函数。

#include <signal.h>
void (*signal(int signo, void (*handler)(int)))(int);

说明:

以下是个示例,捕捉处理了Ctrl+C事件(SIGINT信号),要终止程序,可按下Ctrl+\(SIGQUIT信号)。

#include <unistd.h>
#include <signal.h>
#include <stdio.h>
void INTHandle(int signo) {
    puts("Ctrl+C pressed");
}
int main() {
    signal(SIGINT, INTHandle);
    for (;;) pause();
    return 0;
}

sigaction函数比signal更为复杂,但功能更强,用法灵活,允许在获取信号处理的同时无需将其改变,还可设置各种属性对调用信号处理函数时的行为施以更精准的控制。

#include <signal.h>
int sigaction(int signo, const struct sigaction *act, struct sigaction *oldact);

其中:

sigaction结构定义主要如下:

struct sigaction {
    void (*sa_handler)(int);    /* address of handler */
    sigset_t sa_mask;    /* signals blocked during handler invocation */
    int sa_flags;    /* flags controlling handler invocation; */
    void (*sa_restorer)(void);  /* not for application use */
};

其中:

以下是与信号集操作相关的接口:

#include <signal.h>
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signo);
int sigdelset(sigset_t *set, int signo);
int sigismember(sigset_t *set, int signo);

下面是用sigaction重写上述例子。

#include <unistd.h>
#include <stdio.h>
#include <signal.h>
void INTHandler(int signo) {
    puts("Ctrl+C pressed");
}
int main() {
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_handler = INTHandler;
    act.sa_flgas = 0;
    sigaction(SIGINT, &act, NULL);
    for (;;) pause();
    return 0;
}
Table of Contents