我有一个通过信号(特别是 SIGUSR1/SIGUSR2/SIGSTOP)与工作人员通信的应用程序。
我可以相信无论发生什么情况,每个信号都会由处理程序传递和处理吗?
如果信号发送速度快于应用程序处理信号的速度(例如,由于目前主机负载较高),会发生什么情况?
答案1
除了“信号太多”问题之外,还可以明确忽略信号。从man 2 signal
:
If the signal signum is delivered to the process, then one of the
following happens:
* If the disposition is set to SIG_IGN, then the signal is ignored.
信号也可以被阻止。从man 7 signal
;
A signal may be blocked, which means that it will not be delivered
until it is later unblocked. Between the time when it is generated
and when it is delivered a signal is said to be pending.
被阻止和被忽略的信号集均由子进程继承,因此应用程序的父进程可能会忽略或阻止这些信号之一。
如果在进程完成处理之前的信号之前传递多个信号,会发生什么情况?这取决于操作系统。上面链接的联机signal(2)
帮助页对此进行了讨论:
- 系统 V 会将信号处理重置为默认值。更糟糕的是,快速传递多个信号会导致递归(?)调用。
- BSD 会自动阻塞信号,直到处理程序完成。
- 在 Linux 上,这取决于为 GNU 设置的编译标志
libc
,但我期望 BSD 行为。
答案2
您不能相信发送的每个信号都会被传递。例如,Linux 内核“合并”SIGCHLD如果进程需要很长时间来处理退出子进程的 SIGCHLD。
为了回答问题的另一部分,如果许多不同的信号在太短的时间内到达,信号就会在内核内部“排队”。
您应该使用的成员sigaction()
来设置信号处理程序,并仔细设置参数的成员。我认为这意味着至少屏蔽所有“异步”信号。根据 Linux 的手册页,您还将屏蔽正在处理的信号。我认为你应该将成员设置为 SA_SIGINFO,但我不记得为什么我有这种迷信。我相信这将使您的进程获得一个信号处理程序,该处理程序在没有竞争条件的情况下保持设置,并且不会被大多数其他信号中断。sa_sigaction
siginfo_t
sa_mask
siginfo_t
sigaction()
sa_flags
非常非常仔细地编写信号处理函数。基本上只需设置一个全局变量来指示捕获到信号,然后让进程的其余部分处理该信号所需的操作。这样信号将被屏蔽最短的时间。
此外,您还需要非常彻底地测试您的信号处理代码。将其放入一个小型测试进程中,并发送尽可能多的 SIGUSR1 和 SIGUSR2 信号,可能来自 2 或 3 个专用信号发送程序。在您确信代码可以快速正确地处理 SIGUSR1 和 SIGUSR2 后,还可以混合一些其他信号。为困难的调试做好准备。
如果您使用 Linux 并且仅使用 Linux,您可能会考虑使用它signalfd()
来创建一个文件描述符,select()
或者轮询以接收这些信号。使用signalfd()
可能会使调试更容易。
答案3
信号保证被传递,从某种意义上说,如果进程成功调用kill
,那么目标将收到信号。这是异步的:发送者无法知道何时接收或处理信号。然而,这并不能保证信号一定会被传递。目标可能会在处理信号之前死亡。如果目标在发送信号时忽略该信号,则该信号将无效。如果目标在处理它们之前接收到相同信号编号的多个实例,则信号可能(并且通常)合并:如果您向进程发送相同的信号两次,您将无法知道该进程是否会收到该信号一次或两次。信号主要是为了杀死进程或作为使进程注意的一种方式而设计的,它们本身并不是为了通信而设计的。
如果您需要可靠的交付,那么您需要不同的通信机制。进程间的通信机制主要有两种:管道允许单向通信; A插座允许双向通信和到同一服务器的多个连接。如果您需要目标处理与您发送的通知一样多的通知,请通过管道发送字节。
答案4
如果在阻塞时传递了多个标准信号,则内核可以自由地合并标准信号。另一方面,实时信号则没有同样的缺陷。
实时信号有以下特点:
- 实时信号的多个实例可以排队。相比之下,如果标准信号的多个实例在该信号当前被阻塞时被传递,则只有一个实例排队。
尝试使用数字在 SIGRTMIN 到 SIGRTMAX 范围内的信号。