Harsha Harsha - 2 months ago 5x
C Question

How to wait for each process to terminate in the following example?

The program terminates after taking 4 or 5 values from

But I want it to accept 8 values (totaling 8 processes) and then terminate.

void main() {

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



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.

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.

but the processes relationships are not the same

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[3]---exit();
          |          |       
          |          proc[2]--fork()----exit();
          |                   |
          |                   proc[4]---exit();
                     |        |
                     |        proc[5]---exit();


  • 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).

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);

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

int main()
    while(wait(NULL) == 0) 

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

|  |
|  +--proc[5]
|  |
|  `--proc[6]
|     |
|     `--proc[7]
|  |
|  `--proc[4]

Suppose I write the following code:

int main()
    fork(); fork(); fork();
    wait(0); wait(0); wait(0);

The result is:

                                ok             ok         ok
     |   |   |                  ^              ^          ^
     |   |   |      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 |
            |   |                  ^              ^
            |   |      err err err |              |
            |   +-p[5]-w()-w()-w()-exit();        |
            |                         ok  err err |
                   |                  ^
                   |      err err err |


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:

  • 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.

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());
    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);
    case 0:
        printf("pid=%d, prnt=%d, pgrp=%d: read: EOF\n",
                getpid(), getppid(), getpgrp());
        printf("pid=%d, prnt=%d, pgrp=%d: read: [%*.*s]\n",
                getpid(), getppid(), getpgrp(), i, i, line);
#if 0
} /* main */