1 signal介绍
先看下函数声明:
#include <signal.h>
//若成功,返回该Unix系统信号之前的信号处理函数地址;若失败,返回SIG_ERR
void (*signal(int signo, void (*func)(int)))(int);
signo参数是Unix系统信号。
func的值是常量SIG_IGN,常量SIG_DFL或当收到此信号后要调用的函数地址。若指定SIG_IGN,则向内核表示忽略此信号(注意,SIGKILL和SIGSTOP不能忽略);若指定SIG_DFL,则表示接收到此信号的动作是系统默认动作;若指定信号处理函数时,我们称这种处理为捕捉该信号。
使用如下的typedef可简化声明:
typedef void Sigfunc(int);
Sigfunc *signal(int , Sigfunc *);
注意:signal函数的形参表示的函数指针指的是对该Unix系统信号设置的信号处理函数;
signal函数的返回值表示的函数指针表示该Unix系统信号之前的信号处理函数。
而函数指针的返回值为空,形参为一个int值。
如果查看头文件<signal.h>,可以找到如下声明:
#define SIG_ERR (void (*)())-1
#define SIG_DFL (void (*)())0
#define SIG_IGN (void (*)())1
这些常量表示"指向函数的指针,该函数要求一个整型参数,而且无返回值"。
2 使用signal函数捕获信号并处理
如下程序捕捉两个用户定义的信号。
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
static void sig_usr(int); /* one handler for both signals */
int main(void)
{
void (*func1)(int);
void (*func2)(int);
if ( (func1 = signal(SIGUSR1, sig_usr)) == SIG_ERR) {
printf("can't catch SIGUSR1 \n");
} else {
printf("catch SIGUSR1 func1:%p \n", func1);
// signal返回值表示对信号SIGUSR2之前的信号处理函数,SIG_DFL
if (func1 == SIG_DFL) {
printf("func1 == SIG_DFL \n");
}
}
if ( (func2 = signal(SIGUSR2, sig_usr)) == SIG_ERR) {
printf("can't catch SIGUSR2 \n");
} else {
printf("catch SIGUSR2 1 func2:%p \n", func2);
// signal返回值表示对信号SIGUSR2之前的信号处理函数,SIG_DFL
if (func2 == SIG_DFL) {
printf("func2 == SIG_DFL \n");
}
}
if ( (func2 = signal(SIGUSR2, sig_usr)) == SIG_ERR) {
printf("can't catch SIGUSR2 \n");
} else {
printf("catch SIGUSR2 2 func2:%p \n", func2);
// signal返回值表示对信号SIGUSR2之前的信号处理函数,sig_usr
if (func2 != NULL) {
func2(77);
}
}
for ( ; ; ) {
pause();
printf("after pause <<<<<<< \n");
}
}
static void sig_usr(int signo) /* argument is signal number */
{
if (signo == SIGUSR1)
printf("received SIGUSR1\n");
else if (signo == SIGUSR2)
printf("received SIGUSR2\n");
else
printf("received signal %d\n", signo);
}
pause函数会使调用函数在接收到一个信号前挂起。
我们使该程序在后台运行,并用kill命令将信号发送给它。
$ ./sigusr &
[2] 4015
catch SIGUSR1 func1:(nil)
func1 == SIG_DFL
catch SIGUSR2 1 func2:(nil)
func2 == SIG_DFL
catch SIGUSR2 2 func2:0x4006f7
received signal 77
$ kill -USR1 4015
received SIGUSR1
after pause <<<<<<<
$ kill -USR2 4015
received SIGUSR2
after pause <<<<<<<
$ kill 4015
该程序不捕捉SIGTERM信号,对该信号的系统默认动作是终止。
3 注意
1 启动程序
exec函数将原来设置为要捕获的信号都更改为默认动作,因为信号捕获函数的地址在所执行的新的程序文件中已无意义。
2 创建进程
当一个进程调用fork时,其子进程继承父进程的信号处理方式。因为子进程开始复制了父进程的内存映像,所以信号捕获函数的地址在子进程中是有意义的。