在下面的示例中,如何等待每个进程终止? [英] How to wait for each process to terminate in the following example?

查看:72
本文介绍了在下面的示例中,如何等待每个进程终止?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

The program terminates after taking 4 or 5 values from scanf. But I want it to accept 8 values (totaling 8 processes) and then terminate.

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

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

    wait(0);
}

推荐答案

好吧,您必须对自己回答的第一个问题是您认为那里有多少个流程?使用fork(2)系统调用返回的值,即使您是父项还是子项,在执行每个fork时也不知道.

Well, the first question you have to answer to yourself is how many processes you think you have there? As you don't use the returned value from the fork(2) system call, you don't know even if you are the parent or you are the child after execution of each fork.

在故事开始时,您将拥有一个进程,该进程进行fork(2)系统调用并转换为两个进程,然后再执行(父级和子级)第二个fork(2)调用,在第二个调用返回时,在两个进程中转换(每个)(另外两个,所以总共四个).然后是第三个,它再次重复了进程的数量,因此您将得到故事的结尾,在完全填充到高度3的二叉树执行历史层次结构中,所有 8 个正在运行的进程.

You'll have one process at the beginning of the tale, which does a fork(2) system call and converts in two processes, that then execute (both, parent and child) a second fork(2) call, converting (each) in two processes (two more, so four in total) when the second call returns. Then comes the third, which duplicates again the number of processes, so you'll get at the end of the story eight running processes at all in a binary tree execution history hierarchy fully populated up to height three.

每个拥有子代的进程都可以每个wait(2),但是有些进程自成立之初就形成了三个,两个,一个,或者根本没有fork() .根节点是唯一产生三个fork()的节点,因此它最多可以执行三个wait(2)而不会发生错误(对于每个子节点),第一个子节点仅产生两个,第二个子节点仅产生一个像这样:

The processes can each wait(2) for each of their own children, but there are processes that, since their inceptions have made three, two, one, or no fork() at all. The root node is the only that makes three fork()s, so it can do up to three wait(2)s without error (for each of its children), the first of its children, makes only two, the second makes only one... like this:

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).
  • proc[0] can wait(2) to proc[1], proc[2] and proc[3];
  • proc[1] can wait(2) to proc[5] and proc[6];
  • proc[2] can wait(2) to proc[4] only
  • proc[3] cannot wait(2) (it will result in error if wait(2) is called);
  • proc[4] cannot wait(2);
  • proc[5] cannot wait(2);
  • proc[6] can wait(2) only to proc[7] and
  • proc[7] cannot wait(2).

由于wait(2)仅可以等待其中一个子级(子级必须使用fork创建,否则调用将导致错误),因此您必须发出与 fork(2) s一样多的wait(2)调用已发出,以等待所有这些,因此您必须控制您拥有的子代的数量(如您所见,此数字对于每个进程都是不同的).例如,您可以在父代中增加计数器(从fork(2)接收0结果的过程,因此您知道到目前为止已发行的fork(2)的数量.

As wait(2) can only wait for one of such children (children must be created with fork, or the call will result in error) you have to issue as many wait(2) calls as fork(2)s you have issued, to wait for them all, so you must control the number of children you have (as you see, this number is different for each process). You can, for example, increment a counter in the parent (the process that receives a 0 result from fork(2) so you know the number of fork(2)s you have issued up to now.

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)直到系统调用导致错误(您没有更多的孩子)

or simply, you can wait(2) until the system call results in an error (you don't have more children)

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

请注意,因为流程层次结构是与二元历史记录树不同的树.流程层次结构是这样的:

Be careful as the process hierarchy is a different tree from the binary history tree. The process hierarchy is like this:

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

假设我编写了以下代码:

Suppose I write the following code:

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)内核将它们作为 zombie进程保留在进程表中(但未分配任何资源),只是为了等待父级执行正确的wait(2)系统调用.这就是为什么内核知道您可以执行wait(2)的原因(您只能等到已创建fork(2)的情况下才能等待).

NOTE

Even if the children die before the parent does a wait(2) the kernel maintains them in the process table (but without any resource allocated) as zombie processes just to wait for the parent to do the proper wait(2) system call. This is why the kernel knows that you can do a wait(2) or not (you can only wait if you have made a fork(2)).

为什么该过程仅完成部分结果并完成?好吧,我一直在阅读一些文档并进行一些测试,并且在我测试过的三种操作系统上的行为是不同的:

Why the process only read(2)s part of the results and finishes? Well, I have been reading some documentation and making some tests, and the behaviours are different on the three operating systems I have tested on:

  • MacOSX.我进行了测试,当进程组负责人exit(2)时,其所有子级均从read(2)调用中唤醒,并给出了ETIMEDOUT错误(令人惊讶)
  • FreeBSD.在FreeBSD上,结果相似,但有不同的错误(EIO).
  • Linux.在Linux上,终端驱动程序在原始模式下仅向每个进程提供一个输入字符(即使有更多字符)(它们在读取时输出每个字符)(这是迄今为止最怪异的行为).
  • MacOS X. I have tested and when the process group leader exit(2)s, all of its children are awaken from the read(2) call and given a ETIMEDOUT error (surprisingly)
  • FreeBSD. On FreeBSD, the result is similar but with a different error (EIO).
  • Linux. On Linux, the terminal driver gives each process only one input character (even if there are more) in raw mode (they output each caracter as it is read) This is by far the most weird behaviour.

由于正常的作业控制使外壳程序重新获得控制终端,并且进程组不再是终端设备的控制组,因此终端应使用错误代码唤醒所有试图从中进行read(2)的进程.也许FreeBSD是我们应该获得的最一致的结果.

As normal job control makes the shell to reacquire the control terminal and the process group ceases to be the control group of the terminal device, the terminal should awake all the processes trying to read(2) from it with an error code, so perhaps FreeBSD is the most coherent result we should have.

用于测试这种情况的代码如下:

The code used to test this case follows:

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <signal.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/wait.h>

void hndlr(int sig)
{
    printf("%d(prnt=%d,pgrp=%d): signal %d received\n",
            getpid(), getppid(), getpgrp(), sig);
}

int main()
{
    int i;
    char line[1024];

    for (i = 1; i < 64; i++)
        signal(i, hndlr);
    printf("pid=%d, pgrp=%d\n", getpid(), getpgrp());
    fork();
    fork();
    fork();
    i = read(0, line, sizeof line);
    switch(i) {
    case -1:
        printf("pid=%d, prnt=%d, pgrp=%d: read: %s(errno=%d)\n",
                getpid(), getppid(), getpgrp(), strerror(errno), errno);
        break;
    case 0:
        printf("pid=%d, prnt=%d, pgrp=%d: read: EOF\n",
                getpid(), getppid(), getpgrp());
        break;
    default:
        printf("pid=%d, prnt=%d, pgrp=%d: read: [%*.*s]\n",
                getpid(), getppid(), getpgrp(), i, i, line);
        break;
    }
#if 0
    wait(NULL);
    wait(NULL);
    wait(NULL);
#endif
} /* main */

这篇关于在下面的示例中,如何等待每个进程终止?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆