如何在以下示例中等待每个进程终止?

时间:2016-08-03 16:32:23

标签: c linux fork wait

scanf获取4或5个值后,程序终止。 但我希望它接受8个值(总共8个进程)然后终止。

void main() {
    fork();
    fork();
    fork();

    scanf("%d",&j);
    printf("The values are %d\n",j);

    wait(0);
}

3 个答案:

答案 0 :(得分:2)

嗯,你必须回答的第一个问题是你认为你有多少个进程?因为你没有使用fork(2)系统返回的值打电话,你不知道即使你是父母,或者在执行每个分支后你是孩子。

您将在故事的开头有一个进程,进行fork(2)系统调用并转换为两个进程,然后执行(父进程和子进程) )第二个fork(2)调用,在第二个调用返回时,在两个进程中转换(每个)两个进程(总共两个,总共四个)。然后是第三个,它再次复制了进程的数量,因此您将在故事的最后得到八个在二进制树执行历史层次结构中完全运行的进程,完全填充到高度3。

但进程关系不一样

每个拥有子项的流程都可以wait(2),但是有一些流程,因为他们的原因已经制作了三个,两个,一个或根本没有fork() 。根节点是唯一生成三个fork()的节点,因此最多可以执行三个wait(2)而没有错误(对于它的每个子节点),它的第一个子节点只生成两个,第二个只有一个...像这样:

proc[0]---fork()-----fork()---fork()----exit();
          |          |        |
          |          |        proc[3]---exit();
          |          |       
          |          proc[2]--fork()----exit();
          |                   |
          |                   proc[4]---exit();
          |
          proc[1]----fork()---fork()----exit();
                     |        |
                     |        proc[5]---exit();
                     |
                     proc[6]--fork()----exit();
                              |
                              proc[7]---exit();

所以

  • proc[0]可以wait(2)改为proc[1]proc[2]proc[3];
  • proc[1]可以wait(2)proc[5]proc[6];
  • proc[2]只能wait(2)proc[4]
  • proc[3]无法wait(2)(如果调用wait(2),则会导致错误);
  • proc[4]无法wait(2);
  • proc[5]无法wait(2);
  • proc[6]只能wait(2)
  • proc[7]
  • proc[7]无法wait(2)

由于wait(2)只能等待其中一个孩子(必须使用fork创建子项,或者调用会导致错误),您必须发出与{em> {一样多的wait(2)次调用{1}}你已经发出,等待它们全部,所以你必须控制你拥有的孩子数量(如你所见,这个数字对于每个过程都是不同的)。例如,您可以在父中递增计数器(从fork(2)收到0结果的过程,这样您就可以知道fork(2)的数量了已经发布到现在。

fork(2)

或者只是,您可以int main() { int forks_made = 0; if (fork() > 0) forks_made++; if (fork() > 0) forks_made++; if (fork() > 0) forks_made++; for (i = 0; i < forks_made; i++) wait(NULL); exit(0); } 直到系统调用导致错误(您没有更多孩子)

wait(2)

请注意,因为流程层次结构与二进制历史记录树不同。流程层次结构如下:

int main()
{
    fork();
    fork();
    fork();
    while(wait(NULL) == 0) 
        continue;
    exit(0);
}

假设我写了以下代码:

proc[0]
|
+--proc[1]
|  |
|  +--proc[5]
|  |
|  `--proc[6]
|     |
|     `--proc[7]
|
+--proc[2]
|  |
|  `--proc[4]
|
`--proc[3]

结果是:

int main()
{
    fork(); fork(); fork();
    wait(0); wait(0); wait(0);
    exit(0);
}

即使孩子们在之前死去,父母做了 ok ok ok p[0]-f()-f()-f()----------------w()------------w()--------w()-exit(); | | | ^ ^ ^ | | | err err err | | | | | +-p[3]-w()-w()-w()-exit(); | | | | | | | | ok err err | | | +-p[2]-f()----------------w()-w()-w()-exit(); | | | ^ | | | | | | | err err err | | | +-p[4]-w()-w()-w()-exit(); | | ok ok err | +-p[1]-f()-f()----------------w()------------w()-w()-exit(); | | ^ ^ | | err err err | | | +-p[5]-w()-w()-w()-exit(); | | ok err err | +-p[6]-f()----------------w()-w()-w()-exit(); | ^ | err err err | +-p[7]-w()-w()-w()-exit(); 内核在进程表中维护它们(但没有分配任何资源)作为僵尸进程只是等待父母进行正确的wait(2)系统调用。这就是内核知道您可以执行wait(2)的原因(您只能等待wait(2))。

注2

为什么流程只有fork(2)部分结果并完成?好吧,我一直在阅读一些文档并进行一些测试,并且我测试过的三个操作系统的行为有所不同:

  • MacOS X.我已经过测试,当流程组负责人read(2)时,其所有子项都从exit(2)调用中唤醒并出现read(2)错误(令人惊讶)
  • FreeBSD的。在FreeBSD上,结果类似但有不同的错误(ETIMEDOUT)。
  • Linux操作系统。在Linux上,终端驱动程序在原始模式下只给每个进程一个输入字符(即使有更多)(它们在读取时输出每个字符)这是迄今为止最奇怪的行为。

由于正常作业控制使shell重新获取控制终端并且进程组不再是终端设备的控制组,终端应该使用错误代码唤醒尝试EIO的所有进程所以也许FreeBSD是我们应该拥有的最连贯的结果。

用于测试此案例的代码如下:

read(2)

答案 1 :(得分:1)

这里的问题是wait(0)只等待第一个孩子。一旦成功竞争获得自己的输入,主要过程将退出,直接的孩子也做了同样的事情。

最简单的方法是等到没有更多的孩子等待:

void main() {
    int j;
    fork();
    fork();
    fork();

    scanf("%d", &j);
    printf("The values are %d\n",j);

    while (wait(0) != -1);
}

按预期从交互式终端读取8个值。

你永远不应该在真正的程序中这样做。主线程应该读取所​​有8个值并根据需要创建子进程来处理它们。如果不这样做会导致竞争条件和缓冲问题。

以下是一个例子,为简洁起见,少用fork()

$ for i in {1..4}; do echo "$i"; done  | ./foo
The values are 1
The values are 0
The values are 0
The values are 0

$ for i in {1..4}; do echo "$i"; sleep 0.1; done  | ./foo
The values are 1
The values are 2
The values are 3
The values are 4

在前一种情况下,对管道的多次写入变为单个读取,由任意进程拾取。其他人一无所获。

在第二种情况下,添加一个短暂的睡眠以在每次读取之间留出更多时间,现在它们可以获得预期的值。

答案 2 :(得分:0)

您需要等待所有孩子,您的孩子需要等待孩子等。目前您的wait(0)电话只等待一个孩子改变状态。

// wait(0); 
while (1) {
   int status;
   pid_t done = wait( &status );
   if ( done == -1 )
      break; // no more children
}