Linux中的信号(signal)是一个非常实用的功能,进程间能发送信号,内核也能向进程发送信号。本文总结了在进行信号编程时容易忽略的几个问题,方便后续开发过程中查阅。

在信号回调函数中操作互斥锁

信号回调函数是用户自定义的一个函数,但它在运行时既不是内核态也不是用户态,当程序运行在内核态时信号不会响应,在用户态时所有的信号都已经处理完成。

这个特性使得信号回调函数在编写时需要额外小心。例如在信号回调函数中是不能操作互斥锁的,若在信号回调函数中操作了互斥锁,可能会导致程序卡死。

这涉及到信号处理函数安全(signal handler safe)的概念,它与线程安全(thread safe)概念相似,也列举了所有能够在信号处理函数中执行的系统函数。

如果真的需要在信号回调函数中操作互斥锁,妥协的方法是在回调函数中创建一个线程,在这个新创建出的线程中操作互斥锁。这个方案的缺陷是线程可能会创建失败,如需高频地接收和处理信号就需要频繁地创建和销毁线程,可能会由于超过电脑的处理性能,最终导致线程创建失败。

相关博文:在Linux的信号处理函数中不要进行锁相关操作

信号屏蔽字 signal mask

如果不希望处理某个信号,可以通过设置信号屏蔽字(signal mask)暂停信号。

可以使用 sigprocmask 查看和修改当前程序的信号屏蔽字。信号屏蔽字可以根据需要增加和取消。如果在取消信号屏蔽字时,程序有待处理的(pending)信号,这个信号会在 sigprocmask 函数返回之前就被发送到程序了。所以,希望通过组合 sigprocmasksigtimedwait 两个函数实现信号延迟处理的方案是行不通的,原因是在 sigprocmask 取消信号屏蔽字后,信号会在 sigtimedwait 开始等待之前就已经发送过来了。

Signal handlers are per process, signal masks are per thread.

上面这句话摘自 stackoverflow 上的某个回答,我认为说的很好。在多线程程序中每个线程有各自的信号屏蔽字,每个线程的信号屏蔽字只对本线程生效。多线程程序没有一个统一的程序级(process level)的信号屏蔽字,并且在多线程程序中使用 sigprocmask 是未定义行为(undefined behaviour),应该使用对应 pthread_sigmask 函数。