在下面的代码中,我期望控制台打印十个SIGCHLD catch
。我已经通过将 sa_flags
设置为 SA_SIGINFO
并使用 sa_sigaction
而不是 sa_handler
对 SIGCHLD 进行了排队。然而,似乎有些 SIGCHLD 丢失了。为什么?
我认为 fork()
可能会被 SIGCHLD
中断,因此我使用 SA_RESTART
重新启动 fork()
。我在不同的计算机上运行同一段代码。在我的 MacBook 上,显示[1] 24481非法硬件指令
。在另一台 Linux 计算机上,打印的 SIGCHLD catch
少于 10 个。
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define CHECK(syscall, msg) do { \
if ((syscall) == -1) { \
perror(msg); \
} \
} while(0)
void catch(int signo, siginfo_t *info, void *context) {
if (signo == SIGCHLD) {
printf("SIGCHLD caught\n");
}
}
int main () {
sigset_t new_set;
sigemptyset(&new_set);
sigaddset(&new_set, SIGCHLD);
struct sigaction act;
act.sa_sigaction = catch;
act.sa_mask = new_set;
act.sa_flags = SA_SIGINFO | SA_RESTART;
CHECK(sigaction(SIGCHLD, &act, NULL), "sigaction error");
int pid, i;
for (i = 0; i < 10; i++) {
pid = fork();
if (!pid) return;
}
while (1);
}
最佳答案
SIGCHLD
是一种标准信号,这意味着多次出现的信号会合并为一个。 Linux 内核维护标准信号的位集,每个信号一位,并支持对一个关联的 siginfo_t
进行排队。
修复:
void catch(int signo, siginfo_t*, void*) {
int status;
pid_t pid;
if(signo == SIGCHLD) {
while((pid = waitpid(-1, &status, WNOHANG)) > 0)
printf("child %u terminated.\n", (unsigned)pid);
}
}
另请注意,您不需要显式阻止您处理的信号,因为它会自动为您阻止,除非使用 SA_NODEFER
标志。
而且,迂腐地说,信号处理程序中只能使用有限数量的异步信号安全函数(请参阅 man signal-safety
),printf
不是其中之一。
关于无法接收到所有的SIGCHLD,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48750382/