3.5. 信号

信号在类 Unix 操作系统世界中是一种简单的“中断”形式,并且是 Unix 的古老组成部分。一个进程可以对另一个进程设置“信号”(例如使用 kill(1) 或 kill(2)),而另一个进程将异步接收和处理该信号。为了使一个进程有权向另一个进程发送任意信号,发送进程必须具有 root 权限,或者发送进程的真实或有效用户 ID 必须等于接收进程的真实或保存的 set-user-ID。但是,某些信号可以通过其他方式发送。特别是,SIGURG 可以通过 TCP/IP 带外 (OOB) 消息通过网络传递。

尽管信号是 Unix 的古老组成部分,但它们在不同的实现中具有不同的语义。基本上,它们涉及诸如“在处理另一个信号时发生信号会怎样?”之类的问题。较旧的 Linux libc 5 对于某些信号操作使用了与较新的 GNU libc 库不同的一组语义。在信号处理程序中调用 C 库函数通常是不安全的,甚至某些系统调用也是不安全的;您需要检查您所做的每个调用的文档,以查看它是否承诺在信号内部调用是安全的。有关更多信息,请参阅 glibc FAQ(在某些系统上,本地副本位于/usr/doc/glibc-*/FAQ).

对于新程序,只需使用 POSIX 信号系统(它又是基于 BSD 工作的);这个集合被广泛支持,并且没有一些旧的信号系统存在的问题。POSIX 信号系统基于使用 sigset_t 数据类型,可以通过一组操作来操作它:sigemptyset()、sigfillset()、sigaddset()、sigdelset() 和 sigismember()。您可以在 sigsetops(3) 中阅读有关这些内容的信息。然后使用 sigaction(2)、sigprocmask(2)、sigpending(2) 和 sigsuspend(2) 来设置和操作信号处理(有关更多信息,请参阅它们的手册页)。

一般来说,使任何信号处理程序都非常简短和简单,并仔细查找竞争条件。信号本质上是异步的,因此很容易引起竞争条件。

服务器存在一个常见的约定:如果您收到 SIGHUP,则应关闭所有日志文件,重新打开并重新读取配置文件,然后重新打开日志文件。这支持在不停止服务器的情况下进行重新配置,并在不丢失数据的情况下进行日志轮换。如果您正在编写一个此约定有意义的服务器,请支持它。

Michal Zalewski [2001] 撰写了一篇关于如何利用信号处理程序的优秀教程,并就如何消除信号竞争问题提出了建议。我鼓励您查看他的摘要以获取更多信息;以下是我的建议,它们与 Michal 的工作类似