Process Creation

The following example is excerpted from Franco Callari's lecture notes on process creation.
#include <sys/types.h>
#include <sys/wait.h>
#include <stdio.h>

int main(int argc, char *argv[])
{
    pid_t whichone, first, second;
    int howmany;
    int status;
    
    if ((first=fork())==0) /* Parent spawns 1st child */
    {
        printf("Hiya, I am the first child, "
               "and my id is %d\n", 
               getpid()
               );
        sleep(10); /* Sleep 10 sec, then exit */
        exit(0);   
    }
    else if (first == -1)
    {
        perror("1st fork: something went bananas\n");
        exit(1);
    }
    else if ((second=fork())==0) /* Parent spawns 2nd  child */
    {
        printf("Hiya, I am the second child, "
               "and my id is %d\n", 
               getpid()
               );
        sleep(15); /* Sleep 15 sec, then exit */
        exit(0);   
    }
    else if (second == -1)
    {
        perror("2nd fork: something went bananas\n");
        exit(1);
    }
           
    printf("This is the parent\n");
    
    howmany=0; 
    while (howmany < 2) /* Wait twice */
    {
        whichone=wait(&status);
        howmany++;
        
        if (whichone==first)
           printf("First child exited ");
        else
           printf("Second child exited ");
    
        if ((status & 0xffff)==0)
           printf("correctly\n");
        else
           printf("uncorrectly\n");
    }

    return 0;
}

The first part of this example, up to the howmany=0 statement, contains nothing new: just make sure you understand what the instruction flow is in the parent and in the children. The parent then enters a loop waiting for the children's completion. The wait() system call blocks the caller process until one of its immediate children (not children's children, or other siblings) terminates, and then returns the pid of the terminated process. The argument to wait() is the address on an integer variable or the NULL pointer. If it's not NULL, the system writes 16 bits of status information about the terminated child in the low-order 16 bits of that variable. Among these 16 bits, the higher 8 bits contain the lower 8 bits of the argument the child passed to exit() while the lower 8 bits are all zero if the process exited correctly, and contain error information if not (see the wait(2) man page for details). Hence, if a child exits with 0 all those 16 bits are zero. To reveal if this is actually the case we test the bitwise AND expression (status & 0xffff), which evaluates as an integer whose lower 16 bits are those of status, and the others are zero. If it evaluates to zero, everything went fine, otherwise some trouble occurred. Try changing the argument passed to exit() in one of the children.

The Posix and BSD extensions to wait() are useful when a parent must not block waiting for children, but still wants to know about the children's termination status values via the wait mechanism. We'll treat only the Posix waitpid() call, and you are referred to the man page for the BSD call.

The waitpid() call is declared as follows in the sys/wait.h header:

pid_t waitpid(pid_t pid, int *statptr, int options);

Here the meaning of the the return value and of the pointer to the status statptr is exactly the same in wait(). However this call allows you to specify which children should be waited for and how. Specifically, the first argument pid specifies the process(es) that must be waited for. The relevant (for now) cases are:

The relevant (for now) value for the third argument is a constant called WNOHANG, that causes the function not to suspend the caller's execution if status is not immediately available for one of the child processes. This allows to implement a loop in which the parent can do something useful and periodically poll the children's status as well.

Note that the use of a loop also allows to use wait() and yet wait for one particular child: try to figure out yourself how.