fork() and wait() with two child processes

138,820

Solution 1

It looks to me as though the basic problem is that you have one wait() call rather than a loop that waits until there are no more children. You also only wait if the last fork() is successful rather than if at least one fork() is successful.

You should only use _exit() if you don't want normal cleanup operations - such as flushing open file streams including stdout. There are occasions to use _exit(); this is not one of them. (In this example, you could also, of course, simply have the children return instead of calling exit() directly because returning from main() is equivalent to exiting with the returned status. However, most often you would be doing the forking and so on in a function other than main(), and then exit() is often appropriate.)


Hacked, simplified version of your code that gives the diagnostics I'd want. Note that your for loop skipped the first element of the array (mine doesn't).

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>

int main(void)
{
    pid_t child_pid, wpid;
    int status = 0;
    int i;
    int a[3] = {1, 2, 1};

    printf("parent_pid = %d\n", getpid());
    for (i = 0; i < 3; i++)
    {
        printf("i = %d\n", i);
        if ((child_pid = fork()) == 0)
        {
            printf("In child process (pid = %d)\n", getpid());
            if (a[i] < 2)
            {
                printf("Should be accept\n");
                exit(1);
            }
            else
            {
                printf("Should be reject\n");
                exit(0);
            }
            /*NOTREACHED*/
        }
    }

    while ((wpid = wait(&status)) > 0)
    {
        printf("Exit status of %d was %d (%s)\n", (int)wpid, status,
               (status > 0) ? "accept" : "reject");
    }
    return 0;
}

Example output (MacOS X 10.6.3):

parent_pid = 15820
i = 0
i = 1
In child process (pid = 15821)
Should be accept
i = 2
In child process (pid = 15822)
Should be reject
In child process (pid = 15823)
Should be accept
Exit status of 15823 was 256 (accept)
Exit status of 15822 was 0 (reject)
Exit status of 15821 was 256 (accept)

Solution 2

Put your wait() function in a loop and wait for all the child processes. The wait function will return -1 and errno will be equal to ECHILD if no more child processes are available.

Solution 3

brilliant example Jonathan Leffler, to make your code work on SLES, I needed to add an additional header to allow the pid_t object :)

#include <sys/types.h>
Share:
138,820

Related videos on Youtube

Joe
Author by

Joe

Updated on July 09, 2022

Comments

  • Joe
    Joe almost 2 years

    I need to use the fork() and wait() functions to complete an assignment. We are modelling non-deterministic behavior and need the program to fork() if there is more than one possible transition.

    In order to try and work out how fork and wait work, I have just made a simple program. I think I understand now how the calls work and would be fine if the program only branched once because the parent process could use the exit status from the single child process to determine whether the child process reached the accept state or not.

    As you can see from the code that follows though, I want to be able to handle situations where there must be more than one child processes. My problem is that you seem to only be able to set the status using an _exit function once. So, as in my example the exit status that the parent process tests for shows that the first child process issued 0 as it's exit status, but has no information on the second child process.

    I tried simply not _exit()-ing on a reject, but then that child process would carry on, and in effect there would seem to be two parent processes.

    Sorry for the waffle, but I would be grateful if someone could tell me how my parent process could obtain the status information on more than one child process, or I would be happy for the parent process to only notice accept status's from the child processes, but in that case I would successfully need to exit from the child processes which have a reject status.

    My test code is as follows:

    #include <stdio.h>
    #include <unistd.h>
    #include <stdlib.h>
    #include <errno.h>
    #include <sys/wait.h>
    
    int main(void)  {
    
        pid_t child_pid, wpid, pid;
        int status = 0;
        int i;
    
        int a[3] = {1, 2, 1};
        for(i = 1; i < 3; i++)  {
            printf("i = %d\n", i);
            pid = getpid();
            printf("pid after i = %d\n", pid);
            if((child_pid = fork()) == 0)  {
                printf("In child process\n");
                pid = getpid();
                printf("pid in child process is %d\n", pid);
                /* Is a child process */
                if(a[i] < 2)  {
                    printf("Should be accept\n");
                    _exit(1);
                } else  {
                    printf("Should be reject\n");
                    _exit(0);
                }
            }
        }
    
        if(child_pid > 0)  {
            /* Is the parent process */
            pid = getpid();
            printf("parent_pid = %d\n", pid);
            wpid = wait(&status);
            if(wpid != -1)  {
                printf("Child's exit status was %d\n", status);
                if(status > 0)  {
                    printf("Accept\n");
                } else  {
                    printf("Complete parent process\n");
                    if(a[0] < 2)  {
                        printf("Accept\n");
                    } else  {
                        printf("Reject\n");
                    }
                }
            }
        }
        return 0;
    }
    
  • Joe
    Joe about 14 years
    Great. I understand. Thank you so much.
  • Joe
    Joe about 14 years
    Ok. I understand. Thank you very much for your reply.
  • Joe
    Joe about 14 years
    Thanks again for your time. Thats great. I produced a mangled working version of my own after your initial post, but yours is much neater. Cheers
  • Tyler McHenry
    Tyler McHenry about 14 years
    @Joe if this response was the most helpful to you, then you should accept it by clicking the check mark to the left.
  • Jonathan Leffler
    Jonathan Leffler about 14 years
    @Richard: I thought about that, but since the question states that this is a cut-down example to sort out the inter-process synchronization details, I concluded that it was not spoon-feeding the answer to the homework.
  • Richard Pennington
    Richard Pennington about 14 years
    @Jonathan: I was just kidding... It was a good question and yours is a good answer.
  • Jonathan Leffler
    Jonathan Leffler about 11 years
    That's very odd...POSIX 2008 and all the other Linux systems I've come across don't need the <sys/types.h> header. There are functions in <unistd.h> (such as getpid()) that require the type in their declaration, so <sys/types.h> does not need to be included explicitly. Can you state the compiler options you were using that necessitated #include <sys/types.h>?
  • Apache
    Apache almost 11 years
    @JonathanLeffler I know it's an old comment, but SLES is older than the rest. It's like RedHat's EL. So that's the reason I guess.