首页 > 解决方案 > 如何避免 read() 在以下情况下挂起?

问题描述

我有一些分叉第三方应用程序并将其标准输出重定向到父进程的代码,大致如下(为简洁起见,此处没有错误处理):

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()的也有同样的问题。

(后续行动:read() 挂在僵尸进程上

标签: clinuxpipeforkzombie-process

解决方案


您需要专门忽略 SIGCHLD。收割僵尸是你的责任,但在被封锁的情况下你不能这样做read。如果你在被吞下read后打电话,你会永远留在里面。SIGCHLDread


推荐阅读