子进程挂起,即使在关闭其输入后也是如此

Child process hangs, even after closing its input

假设我有这个示例代码(为清楚起见,大部分错误检查都被省略了)

static void c_way() {
    int pipefd[2], ch;
    FILE *rf, *wf;
    pipe(pipefd);
    switch (fork()) {
    case -1:
        /* something went wrong */
        break;
    case 0:
        dup2(pipefd[0], STDIN_FILENO);
        dup2(pipefd[1], STDOUT_FILENO);
        close(pipefd[0]);
        close(pipefd[1]);
        execvp(cat_args[0], cat_args);
        _exit(0);
    }
    rf = fdopen(pipefd[0], "r");
    wf = fdopen(pipefd[1], "w");
    fprintf(wf, "I have %d apples.\n", 5);
    fclose(wf);
    while ((ch = fgetc(rf)) != EOF)
        putchar(ch);
    puts("Done reading.");
    fflush(stdout);
    fclose(rf);
}

其中 cat_args 就是 {"cat", "-", NULL}。出于某种原因,尽管关闭了父进程的管道写入端,但在 fgetc 循环中似乎从未达到 EOF,就好像子进程正在等待更多输入一样。我忘记关闭一些文件描述符了吗?即使不使用文件指针(即原始 POSIX 读取和写入),它仍然挂起。

我看到一些类似的回答问题,所以这可能是重复的。

当我 运行 我看到这段代码时:

[notroot]$ ./c_way 
I have 5 apples.

然后挂起。

这是一场竞赛:您的 parent 进程在 child 进程 exec 的 cat 之前从管道读取自己的 just-written 数据(并将其写入标准输出) .

child 没有读取任何内容——管道已被 parent 排空——因此耐心地阻塞在 stdin 上等待输入。与此同时,parent 耐心地阻塞在管道的读取端,等待永远不会到达的输入。

您需要两个管道:一个将 parent 连接到 child 标准输入,另一个将 child 标准输出连接到 parent。 (另见 socketpair。)