之前我们学习了信号产生的几种方式,信号处理有几种方式:默认处理方式;忽略;捕获。信号捕获,说白了就是执行我们指定的函数,或者捕获到一个信号后执行我们指定的动作。下面详细介绍两个信号捕获操作参数:signal和sigaction。##信号函数函数原型:sighandler_tsignal(intsignum,sighandler_thandler);其中,sighandler的定义如下:typedefvoid(*sighandler_t)(int);功能:注册一个信号捕获函数,即接收到某个信号,执行其注册的回调函数。函数参数:signum:信号编号,尽量用宏代替数字,更适合跨平台;handler:注册的回调函数;功能缺陷:由于历史原因,该功能在不同版本的Unix和Linux系统中不可用,效果可能不同,因此跨平台性不好,尽量避免使用,使用通用性更好的sigaction功能反而。#include#includevoidfunc(){printf("SIGQUITcatched!\n");}intmain(){signal(SIGQUIT,func);while(1);}##sigactionfunction函数原型:intsigaction(intsignum,conststructsigactionact,structsigactionoldact);function函数:类似于signal函数,用来注册一个信号捕获函数;返回值:成功:0;失败:-1,并设置errno;参数:signum:signalnumber,尽量用宏写而不是数字,更适合跨平台;act:传入参数,新的信号捕获方式;oldact:传出参数,旧的信号捕获方式这里要特别注意参数体中的structsigaction结构体,这也是这个函数的难点,详见下文:structsigaction结构体原型:structsigaction{void(*sa_handler)(int);void(sa_sigaction)(int,siginfo_t,void*);sigset_tsa_mask;intsa_flags;void(*sa_restorer)(void);};这个结构体的成员很多,而且很多都是回调函数的形式,让人望而生畏。但实际上,需要掌握的只有三个。首先,sa_restorer和sa_sigaction这两个成员其中一个已经被弃用,另一个很少被使用,所以我们暂时忽略它们,重点关注剩下的三个。(1)sa_handler:指定信号捕获后的处理函数,即注册回调函数。该成员也可以赋值为SIG_IGN,表示忽略信号,或者注册为SIG_DFL,表示执行信号的默认动作。(2)sa_mask:临时阻塞信号集(或信号掩码字)首先看这样一种情况:一个信号注册了回调函数,当内核把这个信号传过来时,会先经过一个阻塞信号集,并阻塞它首先是部分信号。然后执行相应的回调函数。如下图所示:如果这个回调函数的回调执行时间比较长,比如2秒,在这2秒内,还有其他信号过来,那么进程应该挂起当前回调函数去响应新的信号,或者不是新的信号,先处理完当前的回调函数?正确的做法是在回调函数执行过程中使用sa_mask临时替换进程的阻塞信号集,以保证回调函数安全执行,然后取消替换。注意:这个过程只发生在回调函数执行过程中,是一个临时设置。(3)sa_flags:通常设置为0,表示使用默认属性。再来看另一种场景:比如一个进程注册了一个SIGQUIT的回调函数。当回调函数执行时,SIGQUIT函数又来了。这个时候,进程是否响应信号?这是sa_flags的作用,当设置为0时,表示使用默认属性,即不先响应信号,而是在处理信号之前执行回调函数。另外,阻塞的常规信号是不支持排队的,也就是说在回调函数执行过程中,当成千上万个相同的信号再次到来时,系统只会记录一次。后32个实时信号支持排队。#include#include#includevoidfunc(intsignal){printf("SIGQUITcatched!\n");sleep(2);//用来模拟执行callbackfunctionLongtimeprintf("funcfinished!\n");}intmain(){structsigactionact;act.sa_handler=func;sigemptyset(&act.sa_mask);//先清除临时阻塞信号集sigaddset(&act.sa_mask,SIGINT);//回调函数执行过程中屏蔽SIGINTact.sa_flags=0;sigaction(SIGQUIT,&act,NULL);//注册回调函数while(1);return0;}