【问题标题】:Linux fork() and wait()Linux fork() 和 wait()
【发布时间】:2014-04-21 07:14:36
【问题描述】:

我有一个难闻的问题:(

我有这个代码:

int main()
{
    pid_t child, parent;        
    int status=0;
    int i;

    printf("parent = %d\n", getpid());

    for(i=1; i<=5; i++){
        if( (child = fork()) == 0){
            sleep(i);
            printf("i=%d, %d\n",i, getpid());
        }
    }
    wait(0);
    while( (parent = wait(&status)) > 0){
        printf("Exit = %d, child = %d\n", status/256, parent);
    }    
}

输出类似于:

    1, 21320
    2, 21321
    Exit = 0, child = 21321
    3, 21322
    Exit = 0, child = 21322
    4, 21323
    Exit = 0, child = 21323
    5, 21324
    Exit = 0, child = 21324

我认为 wait(0) 不等待所有子进程,而只等待第一次退出并写入所有 (Exit = ...)。

有没有办法做到这一点:

    1, 21320
    2, 21321
    3, 21322
    4, 21323
    5, 21324

    Exit = 0, child = 21320
    Exit = 0, child = 21321
    Exit = 0, child = 21322
    Exit = 0, child = 21323
    Exit = 0, child = 21324

?

【问题讨论】:

  • wait 的手册说:“等待暂停调用进程,直到 一个 子进程结束。”
  • 这个status/256 很时髦。试试 WEXITSTATUS。而 `parent = wait()` 只是令人困惑。它(理想情况下)返回子 pid..

标签: c linux fork wait


【解决方案1】:

这不是wait() 的问题,而是同步问题 - 或缺乏同步。每次调用fork(),子进程都会休眠一段时间,但父进程会继续执行。您的父进程完成其 fork 循环并在大多数子进程仍在睡觉时开始其等待循环,因此每次其中一个子进程退出时,父进程已经在等待它。这就是为什么父进程能够在每个子进程退出后立即打印其退出消息的原因。

如果你想让你的父进程在进入wait()循环之前等待所有子进程完成睡眠,你需要使用IPC同步机制,例如POSIX semaphores,来使父进程阻止,直到所有孩子都表示他们准备好了。


但是,如果您的目标只是让所有退出消息出现在子 ID 消息之后的屏幕上,那么您根本不需要延迟 wait() 调用。只需更改wait 循环以将状态值存储在数组中,而不是立即打印它们,然后在wait 循环完成后,运行另一个 循环以打印数组的内容。

【讨论】:

  • 如果您只是在等待进程退出,为什么还需要额外的同步?
  • @JohnB,OP 似乎想等到 所有 孩子完成后,再为 任何 个孩子调用 wait()。跨度>
  • 他不只是想等待所有孩子退出后再继续在父母中吗?
  • @JohnB,他想在打印任何退出状态之前等待所有孩子退出。退出状态当前是从调用wait() 的循环中打印出来的,因此他试图通过使用wait(0) 来延迟进入整个循环以等待所有孩子。这是一个错误,因为wait(0) 不是这样工作的,但是信号量可以 用来完成同样的事情。我还用一种完全不涉及延迟进入 wait() 循环的替代方法更新了我的答案。
【解决方案2】:

这是按您要求的顺序生成输出的最简单方法的演示。它使用 3 个循环:一个用于创建子进程,一个用于等待子进程并收集其退出状态,另一个用于打印退出状态。

#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

#define NUMPROC 5

int main(void)
{
  pid_t child[NUMPROC];
  int status[NUMPROC];
  int i;

  printf("parent = %d\n", getpid());

  for(i=0;i<NUMPROC;++i) {
    if(fork() == 0) {
      sleep(i);
      printf("i=%d, %d\n",i, getpid());
      _exit(0);
    }
  }

  for(i=0;i<NUMPROC;++i)
    child[i] = wait(&status[i]);

  for(i=0;i<NUMPROC;++i)
    printf("Exit = %d, child = %d\n", WEXITSTATUS(status[i]), child[i]);
}

【讨论】:

    【解决方案3】:

    wait (0) 等待 any 子进程更改状态,而不是等待所有进程更改状态。您必须存储子进程的 pid-s,然后在 for 循环中等待它们中的每一个:

    i_am_child = 0;
    my_i = 0;
    for (i = 0; i < nr_children; ++i) {
        child = fork ();
        if (child == 0) { i_am_child = 1; my_i = i; break; }
        childpid [i] = child;
    }
    if (i_am_child) {
        // do what children are supposed to do, e.g. printf and sleep (my_i)
        exit (0);
    }
    if (! i_am_child) // strictly, this line is not necessary, since children have exited
        for (i = 0; i < nr_children; ++i) waitpid (childpid [i], &status, 0);
    ...
    

    确保分叉和等待仅由父级执行!

    据我所知,孩子们应该在 fork 之后立即break 第一个 for 循环,而不是进入第二个。

    【讨论】:

      【解决方案4】:

      你是对的。当任何孩子退出时,wait 将返回。如果您想等到所有子节点都退出,您可以在 while 循环中重复等待调用,直到它返回 -1 并且 errno = ECHILD,这意味着不再存在子节点。

      while (! (wait (0) == -1 && errno == ECHILD) ) ;
      

      这个循环有点简单。您可能想要检查您的流程的结果,并且您应该处理可能发生的其他错误。

      【讨论】:

        猜你喜欢
        • 2015-03-30
        • 1970-01-01
        • 1970-01-01
        • 2011-02-12
        • 2015-11-08
        • 1970-01-01
        • 1970-01-01
        • 2020-06-18
        • 2012-01-03
        相关资源
        最近更新 更多