【摘自《Linux/Unix系统编程手册》】
Unix系统提供了两种方式来改变信号处置:signal() 和 sigaction()。
signal() 的行为在不同Unix实现间存在差异,这也意味着对可移植性有所追求的程序绝不能使用此调用来建立信号处理函数。故此,sigaction() 是建立信号处理器的首选API(强力推荐)。
信号处理器
信号处理器程序(也称为信号捕捉器)是当指定信号传递给进程时将会调用的一个函数。
调用信号处理器程序,可能会随时打断主程序流程。内核代表进程来调用处理器程序,当处理器返回时,主程序会在处理器打断的位置恢复执行。
signal()
#include <signal.h> void ( *signal(int sig, void (*handler)(int)) ) (int); Returns previous signal disposition on success, or SIG_ERR on error
针对信号处理器函数指针做如下类型定义,有助于理解:
typedef void (*sighandler_t)(int);
signal() 原型可以改写为:
sighandler_t signal(int sig, sighandler_t handler);
在为 signal() 指定handler参数时,可以以如下值来代替函数地址:
SIG_DFL
将信号处置重置为默认值。这适用于将之前signal()调用所改变的信号处置还原
SIG_IGN
忽略该信号。如果信号专为此进程而生,那么内核会默默将其丢弃。进程甚至从未知道曾经产生了该信号
调用 signal() 成功将返回先前的信号处置,有可能是先前安装的处理器函数地址,也可能是常量 SIG_DFL 和 SIG_IGN 之一。如果调用失败,将返回 SIG_ERR。
示例:
1 #include <signal.h> 2 #include "tlpi_hdr.h" 3 4 static void sigHandler(int sig) 5 { 6 static int count = 0; 7 /* UNSAFE: This handler uses non-async-signal-safe functions (printf(), eixt())*/ 8 9 if (sig == SIGINT) { 10 count++; 11 printf("Caught SIGINT (%d) ", count); 12 return; /* Resume execution at point of interruption*/ 13 } 14 15 /* Must be SIGQUIT - print a message and terminate the process*/ 16 17 printf("Caught SIGQUIT - that's all forks! "); 18 exit(EXIT_SUCCESS); 19 } 20 21 int main(int argc, char* argv[]) 22 { 23 if (signal(SIGINT, sigHandler) == SIG_ERR) { 24 errExit("signal"); 25 } 26 if (signal(SIGQUIT, sigHandler) == SIG_ERR) { 27 errExit("signal"); 28 } 29 30 for (;;) 31 pause(); 32 }
sigaction()
#include <signal.h> int sigaction(int sig, const struct sigaction* act, struct sigaction* oldact); Returns 0 on success, or -1 on error
sig参数标识想要获取或改变的信号编号。该参数可以是除去SIGKILL和SIGSTOP之外的任何信号。
act参数是一枚指针,指向描述信号新处理器的数据结构,如果仅对现有处理器感兴趣,可以指定act为NULL。oldact指向当前的处理器,若无意获取此信息,可以指定为NULL。
struct sigaction { union { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); } _sigaction_handler; sigset_t sa_mask; int sa_flags; void (*sa_restorer)(void); }; /* Following defines make the union fields look like simple fields in the parent structure*/ #define sa_handler __sigaction_handler.sa_handler #define sa_sigaction __sigaction_handler.sa_sigaction
sa_handler字段对应于signal()的handler参数。其所指定的值为信号处理器函数的地址,亦或是常量SIG_IGN、SIG_DFL之一。仅当sa_handler是信号处理程序的地址时,即sa_handler的取值在SIG_IGN和SIG_DFL之外,才会对sa_mask和sa_flags字段加以处理,余下的字段sa_restorer,不适用于应用程序。
注:
sa_restorer字段仅供内部使用,用以确保当信号处理器程序完成后,会去调用专用的sigreturn() 系统调用,借此来恢复进程的执行上下文,以便进程从信号处理器中断的位置继续执行。
sa_mask字段定义了一组信号,在调用有sa_handler所定义的处理器程序时将阻塞改组信号。当调用信号处理器程序时,会在调用信号处理器之前,将该组信号中当前未处于进程掩码之列的任何信号自动添加到进程掩码中。这些信号将保留在进程掩码中,直至信号处理器函数返回,届时将自动删除这些信号。利用sa_mask字段可指定一组信号,不允许它们中断此处理器程序的执行。此外,引发对处理器程序调用的信号将自动添加到进程信号掩码中。这意味着,当正在执行处理器程序时,如果同一信号实例第二次抵达,信号处理器程序将不会递归中断自己。由于不会对遭阻塞的信号进行排队处理,如果在处理器程序执行中重复产生这些信号中的任何信号,(稍后)对信号的传递都是一次性的。
sa_flags字段是一个位掩码,指定用于控制信号处理过程的各种选项。该字段包含的位如下(可以相或(|))