即使在关闭输入后,子进程也会挂起

时间:2018-01-10 21:18:23

标签: c pipe fork posix child-process

假设我有这个示例代码(为了清楚起见,错误检查在很大程度上被省略)

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读写),它仍然会挂起。

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

1 个答案:

答案 0 :(得分:1)

当我运行此代码时,我看到:

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

然后挂起。

它是一场竞赛:你的父进程在子进程执行的 cat 之前从管道中读取自己刚刚写入的数据(并将其写入stdout)。

孩子什么都不读 - 管道已被父母排干 - 因此耐心地阻止stdin等待输入。与此同时,父母在管道的读取端耐心地阻塞,等待输入永远不会到达。

您需要两个管道:一个用于将父级连接到子级stdin,另一个用于将子级stdout连接到父级。 (另见socketpair。)