4

i have one, bad smelling problem :(

i have this code:

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

and output is similar to:

    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

And i think that wait(0) not waiting for all subprocess but only wait for first exit and write all (Exit = ...).

Is any way to do this:

    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

?

RAM
  • 2,257
  • 2
  • 19
  • 41
cniak
  • 301
  • 2
  • 6
  • 13
  • 2
    The manual for `wait` says, "wait suspends the calling process until **one** of its child processes ends." – M.M Mar 16 '14 at 00:19
  • Maybe duplicate, http://stackoverflow.com/questions/1510922/waiting-for-all-child-processes-before-parent-resumes-execution-unix – M.M Mar 16 '14 at 00:22
  • 2
    This `status/256` is funky. Try WEXITSTATUS. And ` parent = wait()` is just confusing. It is (ideally) returning the child pid.. – Duck Mar 16 '14 at 00:24

4 Answers4

4

This isn't a problem with wait(), it's a matter of synchronization — or lack thereof. Each time you call fork(), your child process sleeps for awhile, but the parent process continues executing. Your parent process finishes its fork loop and starts its wait loop while most of the children are still sleeping, so each time one of the children exits, the parent is already waiting for it. That's why the parent process is able to print its exit message immediately after each child exits.

If you want your parent process to wait for all the children to finish sleeping before entering the wait() loop, you'll need to need to use an IPC synchronization mechanism, such as POSIX semaphores, to make the parent process block until all children have signaled that they're ready.


However, if your goal is simply to have all the exit messages appear on the screen after the child ID messages, you don't really need to delay the wait() calls at all. Just change the wait loop to store the status values in an array instead of immediately printing them, and then after the wait loop finishes, run another loop to print the contents of the array.

Wyzard
  • 33,849
  • 3
  • 67
  • 87
  • 1
    Why do you need additional synchronization if you're simply waiting for your processes to exit? – JohnB Mar 16 '14 at 00:36
  • @JohnB, the OP seems to want to wait until *all* the children have finished before calling `wait()` for *any* of them. – Wyzard Mar 16 '14 at 00:37
  • Doesn't he simply want to wait for all children to exit before continuing in the parent? – JohnB Mar 16 '14 at 00:46
  • @JohnB, he wants to wait for all children to exit before printing any of their exit statuses. The exit statuses are currently printed from within the loop that calls `wait()`, so he was trying to delay entry to the entire loop by using `wait(0)` to wait for all the children. That was a mistake since `wait(0)` doesn't work that way, but semaphores *can* be used to accomplish the same thing. I've also updated my answer with an alternative approach that doesn't involve delaying entry to the `wait()` loop at all. – Wyzard Mar 16 '14 at 00:54
4

Here's a demo of the easiest way to produce the output in the order you asked for. It uses 3 loops: one to create the child processes, one to wait for them and collect their exit statuses, and one to print the exit statuses.

#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]);
}
2

wait (0) waits for any child process to change status, not for all processes to change status. You have to store the pid-s of your child processes and then wait for each of them in a for loop:

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

Make sure that the forks and waits are executed by the parent only!

As far as I see, the children should break the first for loop immediately after the fork and not enter the second.

JohnB
  • 13,315
  • 4
  • 38
  • 65
2

You are correct. wait will return when any child exits. If you want to wait until all children have exited you can repeat the wait call in a while loop until it returns -1 and errno = ECHILD, which means no more children exist.

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

This loop is a little simplistic. You presumably want to check the result of your processes, and you should handle other errors that could occur.

harmic
  • 28,606
  • 5
  • 67
  • 91