我有一个相当大的应用程序正在处理。作为其工作的一部分,它会生成一些子进程并需要监视它们的状态(正在运行、崩溃)。
SIGCHLD
通过设置使用的信号处理程序来检测子进程死亡signal(2)
。前段时间我把它迁移到了signalfd(2)
.我所做的很简单:
- 删除了信号处理程序
SIGCHLD
- 阻止
SIGCHLD
并创建一个signalfd(2)
捕获SIGCHLD
我的问题是我创建的文件描述符似乎没有捕获SIGCHLD
.但是,如果我忽略该描述符上的调用的返回值read(2)
并调用waitpid(-1, &status, WNOHANG)
I能获取有关退出的子进程的信息。所以看起来通知已发送,但我的signalfd(2)
描述符只是忽略了它。
我确保程序中恰好有一处在描述符read(2)
上被调用signalfd(2)
,恰好有一处被waitpid(2)
调用,并且恰好有一处设置了信号处理。
设置代码如下所示:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGCHLD);
sigprocmask(SIG_BLOCK, &mask, nullptr);
int signal_fd = signalfd(-1, &mask, SFD_NONBLOCK | SFD_CLOEXEC);
if (signal_fd == -1) {
/* log failure and exit */
} else {
/* log success */
}
读取代码如下所示:
signalfd_siginfo info;
memset(&info, 0, sizeof(info));
if (read(signal_fd, &info, sizeof(info)) == -1) {
/*
* Log failure and return.
* The file descriptor *always* returns EAGAIN, even in
* presence of dead child processes.
*/
return;
}
if (info.ssi_signo == SIGCHLD) {
int status = 0;
int child = waitpid(-1, &status, WNOHANG);
/*
* Process result of waitpid(2). The call is successful even if
* the read of signalfd above returned an error.
*/
}
我究竟做错了什么?
编辑:问题是,即使有已死亡的子进程准备好进行-ed,read(2)
也会失败,这意味着 a必须已传递到我的主进程。我知道这可能会返回非阻塞文件描述符,并且代码说明了这一点。EAGAIN
waitpid(2)
SIGCHLD
read(2)
EAGAIN
答案1
当从信号处理迁移时,基于signal(2)
或sigaction(2)
改变signalfd(2)
您接收信号的方式。旧的方式让信号畅通无阻,新的方式则需要阻塞信号。
如果您不想在某些代码区域中受到信号干扰,则需要阻止它们:
sigset_t mask;
sigemptyset(&mask);
sigaddset(&mask, SIGFOO);
pthread_sigmask(SIG_BLOCK, &mask, nullptr);
{
/* not-to-be-disturbed code here */
}
这需要你稍后解锁他们,因为否则将signal(2)
无法sigaction(2)
接他们。
{
/* not-to-be-disturbed code here */
}
pthread_sigmask(SIG_UNBLOCK, &mask, nullptr);
然而,因为signalfd(2)
信号必须保持阻塞。如果您有一个长期被忽视的代码路径,您很少查看它,并且它遵循旧的方式,即阻止和取消阻止某些信号,那么它可能会破坏您从signalfd(2)
.
长话短说在迁移到 时,检查您的代码是否有对 、 等的任何调用,signal(2)
以sigaction(2)
检查您是否没有忘记某些与信号掩码混淆的代码路径。pthread_sigmask(2)
signalfd(2)
(两年半后可能有点晚了,但也许答案会对某人有所帮助。)
答案2
您的read(2)
返回是因为您使用(与)EAGAIN
以非阻塞模式打开文件。signalfd(..., SFD_NONBLOCK | ...)
SFD_NONBLOCK
O_NONBLOCK
如果您想对文件描述符进行阻塞读取,请不要打开文件描述符或将其设置为非阻塞模式。