我正在为需要执行某些子进程并读取其输出的工具箱编写模块。但是,使用该工具包的主程序也可能会产生一些子进程,并为SIGCHLD
建立一个信号处理程序,该程序调用wait(NULL)
来摆脱僵尸进程。因此,如果在我的waitpid()
子程序中创建了子进程,则在调用信号处理程序之前处理子进程,因此信号处理程序中的wait()
将等待下一个进程结束(可能需要永远)。此行为在the man page of waitpid(请参见受赠方2)中进行了描述,因为linux实现似乎不允许wait()
系列处理SIGCHLD
。我试过popen()
和posix_spawn()
,他们都有同样的问题。我也尝试使用双重fork()
,以便直接的孩子立即存在,但我仍然不能保证在接收到SIGCHLD
后调用waitpid()
。我的问题是,如果程序的其他部分设置了一个信号处理程序,它调用wait()
(也许它应该调用waidpid
,但这不是我可以控制的),是否有一种方法可以安全地执行子进程而不会覆盖SIGCHLD
处理程序(因为它可能在某些程序中有用)或任何僵尸进程。如何在主程序中使用`SIGCHLD`处理程序调用`wait()`设置在插件中安全`waitpid()`
一个小程序,这说明问题是在这里(注意,主程序从长远来看孩子退出后唯一的出口,而不是短单,这是它直接与waitpid函数等待()):
#include <signal.h>
#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
static void
signalHandler(int sig)
{
printf("%s: %d\n", __func__, sig);
int status;
int ret = waitpid(-1, &status, 0);
printf("%s, ret: %d, status: %d\n", __func__, ret, status);
}
int
main()
{
struct sigaction sig_act;
memset(&sig_act, 0, sizeof(sig_act));
sig_act.sa_handler = signalHandler;
sigaction(SIGCHLD, &sig_act, NULL);
if (!fork()) {
sleep(20);
printf("%s: long run child %d exit.\n", __func__, getpid());
_exit(0);
}
pid_t pid = fork();
if (!pid) {
sleep(4);
printf("%s: %d exit.\n", __func__, getpid());
_exit(0);
}
printf("%s: %d -> %d\n", __func__, getpid(), pid);
sleep(1);
printf("%s, start waiting for %d\n", __func__, pid);
int status;
int ret = waitpid(pid, &status, 0);
printf("%s, ret: %d, pid: %d, status: %d\n", __func__, ret, pid, status);
return 0;
}
这不起作用的原因很多,除了没有办法知道主程序是否是单线程的:1)我说过我不想重写信号处理程序,因为当我设置了一个不同的信号处理程序(可能是'SIG_IGN')时,主应用程序想要等待的进程可能会在任何时候完成。 2)如果忽略'SIG_CHLD','wait'将返回一个错误,并且不能获得子进程的退出状态,所以我最好设置自己的处理程序而不是忽略它。 – yuyichao
首先,如果程序是单线程的,那么知道它是否是单线程是微不足道的 - 根据定义,它有很多方法可以确保从库中。其次,您将信号处理程序与信号掩码混淆(不同的事情,请阅读sigprocmask的手册页)。第三,即使忽略了CHLD,waitpid也不会返回错误,直到孩子获得成功,所以它可以工作(试用) - 你可能无法可靠地获得退出状态,但可以安全地等待孩子的等待。 –
如果您想在这里找到答案,请正确阅读答案,并在提出答案错误之前尝试解决。另外,如果你问错了问题,如果你的问题得到了错误的答案,不要感到惊讶。 –