Linux的fork()和wait()函数

4
我有一个臭味问题 :(
我有这段代码:
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

?


2
wait 的手册上说,“wait 暂停调用进程,直到其中一个子进程结束。” - M.M
可能是重复的,https://dev59.com/IXI_5IYBdhLWcg3wJPdu - M.M
2
这个 status/256 有点奇怪。试试用 WEXITSTATUS。而且 parent = wait() 只是令人困惑。它(理想情况下)返回的是子进程的 pid。 - Duck
4个回答

4

以下是最简单的按照您要求生成输出的演示。它使用了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]);
}

4
这不是wait()的问题,而是同步-或缺乏同步。每次调用fork()时,子进程会睡一段时间,而父进程则继续执行。当大多数子进程仍在睡觉时,您的父进程完成其分支循环并开始等待循环,因此每次一个子进程退出时,父进程已经在等待它了。这就是为什么父进程能够在每个子进程退出后立即打印其退出消息。
如果您希望父进程在进入wait()循环之前等待所有子进程完成睡眠,则需要使用IPC同步机制,例如POSIX信号量,以使父进程阻塞,直到所有子进程都发出信号,表示它们已准备好。
但是,如果您的目标只是让所有退出消息在子ID消息之后显示在屏幕上,则根本不需要延迟wait()调用。只需将wait循环更改为将状态值存储在数组中而不是立即打印它们,然后在wait循环完成后运行另一个循环,以打印数组内容即可。

1
如果您只是等待进程退出,为什么需要额外的同步? - JohnB
@JohnB,原帖中似乎想等到所有子进程都完成后再调用wait()函数等待任何一个子进程。 - Wyzard
他难道不只是想等待所有子进程退出后再继续在父进程中执行吗? - JohnB
@JohnB,他想要等待所有子进程退出后再打印它们的退出状态。目前,退出状态是从调用wait()的循环内部打印的,因此他试图通过使用wait(0)延迟整个循环的进入来等待所有子进程。那是一个错误,因为wait(0)不起作用,但是信号量可以用来实现相同的效果。我还更新了我的答案,提供了一种完全不涉及延迟进入wait()循环的替代方法。 - Wyzard

2

wait (0) 等待 任何 子进程的状态改变,而不是所有进程的状态都改变。您需要存储子进程的 pid,并在 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和wait操作!

据我所见,子进程应该在fork后立即break第一个循环,不要进入第二个循环。


2

你说得对。当任何子进程退出时,wait将返回。如果您想等到所有子进程都退出,可以在while循环中重复调用wait直到它返回-1并且errno = ECHILD,这意味着没有更多的子进程存在。

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

这个循环有点过于简单化。你可能希望检查你的进程结果,并且应该处理其他可能发生的错误。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接