【问题标题】:Child process hangs, even after closing its input子进程挂起,即使在关闭其输入后也是如此
【发布时间】:2018-06-20 03:26:31
【问题描述】:

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

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

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

【问题讨论】:

  • 这样的例子很多。一件明显错误的事情:孩子既是阅读者又是写作者,这可能不是你想要的。
  • 但如果我确实希望孩子既是读者又是作家怎么办?也就是说,如果我想将数据通过管道传递给一个孩子进行处理,然后读取它的输出。
  • 单个进程读取和写入 SAME 管道是一件很奇怪的事情,你不觉得吗。管道是一个通信通道,至少在两个进程之间。一个进程写入它,另一个进程读取它,这是管理管道的常用方法。似乎在你的情况下你需要两个管道......
  • 更重要的是,只有一个进程应该从管道中读取。如果没有,第一个获取数据,其他的则一无所有。

标签: c pipe fork posix child-process


【解决方案1】:

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

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

然后挂起。

这是一场竞赛:您的父进程在子进程 exec 的 cat 之前从管道中读取自己刚刚写入的数据(并将其写入标准输出)。

孩子什么也没读——管道已经被父母排干了——因此耐心地阻塞在标准输入上等待输入。与此同时,父节点耐心地阻塞在管道的读取端,等待永远不会到达的输入。

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

【讨论】:

  • 哦,所以我从管道中读取得太快了,呵呵。还没有进入 socketpair (这是之前向我建议的一种解决方案,除了普通套接字之外),你的意思是我需要两个 pipefds?
  • @MonatheMonad,是的。在目前的设计中,孩子和父母竞争从管道中读取。您希望父母的输出(到孩子)沿着不是父母输入(来自孩子)的通道传播。因此,两个管道或双向套接字 fd 语义。
猜你喜欢
  • 1970-01-01
  • 2013-08-13
  • 2011-10-12
  • 2021-09-03
  • 1970-01-01
  • 2019-09-11
  • 1970-01-01
  • 1970-01-01
  • 2015-05-07
相关资源
最近更新 更多