Linux的信号处理

Linux为实现信号处理提供了比较多的接口,看似纷杂,但理清信号的关系后还是很有条理的。主要分为以下四组,怎么发送一个信号,收到一个信号后做什么样的处理,主动等待一个信号的发生,对特定信号只记录是否发生供以后再处理;四组的API如下(不考虑多线程):
发送信号 kill, alarm, arise;
安装信号 signal, sigaction;
等待信号 sigsuspend, sigwait,pause;
阻塞信号 sigfillset, sigemptyset, sigaddset, sigdelset, sigismember, sigprocmask;

 

发送信号, kill, alarm, arise
这是唯一一组由信号发送方调用的API,其中alarm, arise都是给自己进程产生信号,而kill是可以从一个进程发送一个信号到另一个进程,因为信号的接收方一般都不做权限检查,所以发送方要有权限给的接收方发送信号信号才能被发送,要不然我就可以写个非法程序,直接使用kill给进程1发送一个SIGKILL信号的话,你的系统基本也就OVER了。这个权限检查的指导原则按我的理解是,发送方的有效用户是否有权限给接收方的实际用户发送信号来决定。因为,当你运行一个seteuid程序,这个程序运行时的euid可能不再是ruid了,但是你仍然可以kill它。arise()的实现我猜就是用kill(getpid(), signo)来实现。另外系统内核检测到一些异常或是状态改变时也会给进程发送信号,如SIGINTR,SIGCHILD等等。

 

安装信号, signal, sigaction
这组API是决定收到一个信号号的处理方法。如果不显示的安装一个信号的处理方法,系统都有对应的默认处理方法,大多数都是直接终止程序本身. 在richard stevens的APUE中说signal的信号处理需要重新安装信号处理方法,但我在FC5下测试,不重新安装也是可以的,并且正在进行信号处理时,发送一个信号过去,该信号也不会丢失,只是暂时阻塞,等前面的信号处理完后再处理。当然当正在信号处理时发送多个信号过去,信号处理完后,可能传递一个信号过去,其它的信号会丢失。至于直接使用signal安装信号,等信号处理完后是否会重启被该信号中断的慢速系统调用,我没有测试,不得而知。 当然你可以使用sigaction来安装信号,并显示的指明restart标志,这样被该信号中断的系统调用就会自动重启。sigaction这个API更复杂,功能也更多灵活,可以随意定制,通过siginfo_t的结构,可以得到很多信号相关的信息。我猜现在的linux的signal都是用sigaction封装来实现的。跟sigaction相比,signal简单多了,没理由不用它,当然,你得保证signal的语义不会对你的程序造成冲突。

 

等待信号 pause, sigsuspend, sigwait
这组API都是等待信号的发生,可以根据程序的实际需求来选取。pause只要接收到信号就会返回,sigsuspend跟sigwait都是只等待某些特定的信号发生,但sigsuspend是把参数里的信号集给阻塞,而sigwait一般是先调用sigprocmask把所有的信号都阻塞,然后再等待sigwait参数里指定的信号。通常sigwait用在多线程程序里,主线程把所有信号都mask掉,然后不同的线程用sigwait来等待自己感兴趣的信号。

 

阻塞信号 sigfillset, sigemptyset, sigaddset, sigdelset, sigismember, sigprocmask
这一组API最多也最简单,前面五个API都是对信号集的操作,用它用得到一个你想要的信号集后就可以调用sigprocmaks的设置程序的信号掩码了。

 

另外, siglongjmp, sigsetjmp是安全的在信号处理程序里跑转的两个接口。

 

最后一些跟信号处理程序里的一些注意事项
1、因为信号会中断慢速的系统调用,所以当系统调用失败时,一定要检查失败原因,当被信号中断时做出重启系统调用的动作;
2、因为信号处理有异步性,所以任何在信号处理程序里调用了不具可重入性的函数都要小心了。这里的不具可重入性的函数据括了系统调用跟自己定义的函数。
3、在信号处理程序里任何可能导致对全局变理的修改也要小心了,一个经常被人忽视的就是在信号处理程序里调用了一个系统调用后可能导致errno的修改。

你可能感兴趣的:(多线程,linux,api,kill,测试,Signal)