【发布时间】:2018-11-30 17:19:15
【问题描述】:
我有一些代码派生了一个第三方应用程序并将其标准输出重定向到父进程,大致如下(为简洁起见,此处没有错误处理):
char* args[] = {"/path/to/3rd/party/binary", "with", "args", NULL};
int fds[2];
pipe2(fds, O_CLOEXEC);
pid_t pid = fork();
if (pid == 0)
{
dup2(fds[1], STDOUT_FILENO);
execvp(args[0], args);
_exit(1);
}
else
{
close(fds[1]);
char buf[1024];
int bytes_read;
while ((bytes_read = read(fds[0], buf, sizeof buf - 1)) > 0)
{
buf[bytes_read] = '\0';
printf("%s", buf);
}
close(fds[0]);
waitpid(pid, NULL, 0);
}
我没有第三方应用程序的代码,它是专有的二进制文件。当在终端中使用与上述代码相同的参数运行第三方应用程序时,它最终会完成。但是,当使用上面的代码 fork 第三方二进制文件时,它并没有完成,而是变成了一个僵尸进程,上面的代码挂在了read() 调用上。
分叉的第三方二进制文件本身分叉了两个守护进程(同样,我无法控制的专有二进制文件),我认为这是造成问题的原因。分叉的守护进程将拥有复制文件描述符的副本,从而阻止 read() 完成。事实上,如果dup2() 调用被替换为:
dup3(fds[1], STDOUT_FILENO, O_CLOEXEC);
子进程完成,但当然没有输出重定向到父进程。此外,当上面的代码被修改为不通过管道向父进程执行任何输出重定向时,子进程会正确完成。
在这种情况下,是否有可能以某种方式防止 read() 调用挂起,还是我需要求助于某种形式的非阻塞 I/O?
更新;使用简单的popen() 也会遇到同样的问题。
【问题讨论】:
-
这是一种可怕的情况。我不知道有任何方法(没有附加调试器)来防止不可修改的应用程序泄漏 FD。超时阅读并添加愤怒的评论怎么样?
-
@thatotherguy,我仍然希望避免这种情况 :) 一个典型的 shell 能够处理这种情况,所以最后一个办法是挖掘一些 shell 代码,看看他们做了什么..跨度>
-
也许你忘了忽略 SIGCHLD?收割僵尸是你的责任。
标签: c linux pipe fork zombie-process