【问题标题】:Child process cannot read after the exiting of parent process父进程退出后子进程无法读取
【发布时间】:2014-02-15 05:21:55
【问题描述】:

通过函数execvp() fork 并执行子程序后,父进程退出。但是这会导致子进程中的函数fgets() 立即返回,而无需等待来自stdin 的输入。

我猜父进程的退出会向子进程发送一些信号,使fgets()函数返回。谁能给我解释一下?

子程序代码:

/* cc child.c -o child */

int main () {
  char buffer[10];
  fgets(buffer, 10, stdin);
  printf("This is what child program read:\n%s", buffer);
}

父程序代码:

/* cc parent.c -o parent */

int main (int argc, char **argv) {
  pid_t pid = fork();
  if (pid < 0) {
    perror("fork");
    exit(EXIT_FAILURE);
  }
  else if (pid == 0) {
    execvp(*(argv+1), argv+1);
  }
  else {
  //    while(1);  if while(1) or wait() is used, child process can wait for input
    exit(1);
  }
}

在 zsh shell 中:

zsh>: ./parent ./child
zsh>: This is what child program read:   // read nothing and print nothing

【问题讨论】:

    标签: c unix process


    【解决方案1】:

    终端由前台进程组控制。当 shell 调用父进程时,它使父进程成为前台进程组的领导者。孩子继承该组并有权访问终端。

    但是,当父进程退出时,shell 会收回对终端的控制权并成为前台进程组的领导者。子进程不再在前台进程组中,因此无法访问终端。在这种情况下,fgets() 将返回 NULL 并设置错误代码。

    如果您从终端以外的其他地方(例如管道)获取输入,那么您会看到程序按预期运行。例如:

    $ echo test | ./parent ./child
    

    所以这个问题只有当输入来自终端时才会出现。

    回想起来,如果检查了 fgets 错误代码,回答这个问题会更直接。在这种情况下,fgets 返回 null,但是您需要检查 feof() 和/或 ferror() 以确定这是否意味着已到达文件末尾(标准输入已关闭)或出现错误。在这种情况下,NULL 表示存在 EIO 错误。

    较早的错误答案(请参阅评论线程以获得解释,由于冗长的讨论而将其留在这里): 当你分叉时,子进程继承标准输入等。当父进程退出时,它关闭标准输入,所以子进程试图从一个关闭的描述符中读取并且什么也得不到。通过添加对wait() 的调用,您可以保持标准输入处于打开状态,这样您的子程序就可以按预期工作。

    【讨论】:

    • 这听起来有道理,但我认为这是错误的(尽管孩子似乎肯定会得到 EOF)。文件描述符对每个进程都是私有的,但它们共享一个打开的文件描述。然而,当一个进程退出时,它的文件描述符被关闭,但是 POSIX 说:当所有与打开的文件描述相关的文件描述符都被关闭时,打开的文件描述应该被释放。退出的父进程应该不能影响子进程中的文件描述符(它们应该保持打开状态)。
    • 如果这是真的,那么为什么即使在子进程终止后父进程仍然能够从stdin 读取?我的意思是孩子在终止时不会关闭stdin吗?
    • @jdigital 嗯,我修改了一下,确实可以阅读!
    • @nitish712 好问题。直到父关闭子进程描述符,子进程才真正终止。由于这里的代码没有显式关闭描述符,这意味着在父级退出之前子级不会关闭(换句话说,在父级退出之前子级不会退出)。您应该可以使用ps 来查看。
    • @JonathanLeffler 我为fork 找到的文档说两个描述符共享打开的文件状态标志(尽管我是从 Google 的顶部结果中获取的,而不是从 Posix 获取的; -)。你的引用说描述符不能被释放(这是有道理的,因为两个进程都指向它),但它并没有说描述符不能被关闭。
    【解决方案2】:

    您应该在打印缓冲区之前检查 fgets() 的返回值。检查 fgets 是否不返回 NULL,然后只打印缓冲区。

    【讨论】:

    • 是的,我应该在打印缓冲区之前检查 fgets() 的返回值,但我不明白为什么它返回 NULL。 @jdigital 给出了答案。
    猜你喜欢
    • 1970-01-01
    • 2011-04-13
    • 2021-10-30
    • 1970-01-01
    • 1970-01-01
    • 2010-09-21
    • 1970-01-01
    • 1970-01-01
    • 2018-08-30
    相关资源
    最近更新 更多