#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); };
通过sa_mask设置信号掩码集。
信号处理函数可以采用void (*sa_handler)(int)或void (*sa_sigaction)(int, siginfo_t *, void *)。到底采用哪个要看sa_flags中是否设置了SA_SIGINFO位,如果设置了就采用void (*sa_sigaction)(int, siginfo_t *, void *),此时可以向处理函数发送附加信息;默认情况下采用void (*sa_handler)(int),此时只能向处理函数发送信号的数值。
sa_falgs还可以设置其他标志:
SA_RESETHAND:当调用信号处理函数时,将信号的处理函数重置为缺省值SIG_DFL
SA_RESTART:如果信号中断了进程的某个系统调用,则系统自动启动该系统调用
SA_NODEFER :一般情况下, 当信号处理函数运行时,内核将阻塞该给定信号。但是如果设置了SA_NODEFER标记, 那么在该信号处理函数运行时,内核将不会阻塞该信号
#include<stdio.h> #include<signal.h> #include<stdlib.h> #include<string.h> #define INPUTLEN 100 void inthandler(int); int main(){ struct sigaction newhandler; sigset_t blocked; //被阻塞的信号集 char x[INPUTLEN]; newhandler.sa_flags=SA_RESETHAND; newhandler.sa_handler=inthandler; sigemptyset(&blocked); //清空信号处理掩码 sigaddset(&blocked,SIGQUIT); newhandler.sa_mask=blocked; if(sigaction(SIGINT,&newhandler,NULL)==-1) perror("sigaction"); else while(1){ fgets(x,INPUTLEN,stdin); //fgets()会在数据的最后附加"\0" printf("input:%s",x); } } void inthandler(int signum){ printf("Called with signal %d\n",signum); sleep(signum); printf("done handling signal %d\n",signum); }
Ctrl-C向进程发送SIGINT信号,Ctrl-\向进程发送SIGQUIT信号。
$ ./sigactdemo ^CCalled with signal 2 ^\done handling signal 2 Quit (core dumped)
由于把SIGQUIT加入了信号掩码集,所以处理信号SIGINT时把SIGQUIT屏蔽了。当处理完SIGINT后,内核才向进程发送SIGQUIT信号。
$ ./sigactdemo ^CCalled with signal 2 ^Cdone handling signal 2
由于设置了SA_RESETHAND,第一次执行SIGINT的处理函数时相当于执行了signal(SIGINT,SIG_DFL),所以进程第二次收到SIGINT信号后就执行默认操作,即挂起进程。
修改代码,同时设置SA_RESETHAND和SA_NODEFER。
newhandler.sa_flags=SA_RESETHAND|SA_NODEFER;
$ ./sigactdemo ^CCalled with signal 2 ^C
在没有设置SA_NODEFER时,在处理SIGINT信号时,自动屏幕SIGINT信号。现在设置了SA_NODEFER,则当SIGINT第二次到来时立即响应,即挂起了进程。