Function

Team-FLY

3.4 The wait Function

When a process creates a child, both parent and child proceed with execution from the point of the fork. The parent can execute wait or waitpid to block until the child finishes. The wait function causes the caller to suspend execution until a child's status becomes available or until the caller receives a signal. A process status most commonly becomes available after termination, but it can also be available after the process has been stopped . The waitpid function allows a parent to wait for a particular child. This function also allows a parent to check whether a child has terminated without blocking.

The waitpid function takes three parameters: a pid , a pointer to a location for returning the status and a flag specifying options. If pid is “1, waitpid waits for any child. If pid is greater than 0, waitpid waits for the specific child whose process ID is pid . Two other possibilities are allowed for the pid parameter. If pid is 0, waitpid waits for any child in the same process group as the caller. Finally, if pid is less than “1, waitpid waits for any child in the process group specified by the absolute value of pid . Process groups are discussed in Section 11.5.

The options parameter of waitpid is the bitwise inclusive OR of one or more flags. The WNOHANG option causes waitpid to return even if the status of a child is not immediately available. The WUNTRACED option causes waitpid to report the status of unreported child processes that have been stopped. Check the man page on waitpid for a complete specification of its parameters.

  SYNOPSIS  #include <sys/wait.h>    pid_t wait(int *stat_loc);    pid_t waitpid(pid_t pid, int *stat_loc, int options);  POSIX  

If wait or waitpid returns because the status of a child is reported , these functions return the process ID of that child. If an error occurs, these functions return “1 and set errno . If called with the WNOHANG option, waitpid returns 0 to report that there are possible unwaited-for children but that their status is not available. The following table lists the mandatory errors for wait and waitpid .

errno

cause

ECHILD

caller has no unwaited-for children ( wait ), or process or process group specified by pid does not exist ( waitpid ), or process group specified by pid does not have a member that is a child of caller ( waitpid )

EINTR

function was interrupted by a signal

EINVAL

options parameter of waitpid was invalid

Example 3.12

The following code segment waits for a child.

 pid_t childpid; childpid = wait(NULL); if (childpid != -1)    printf("Waited for child with pid %ld\n", childpid); 

The r_wait function shown in Program 3.3 restarts the wait function if it is interrupted by a signal. Program 3.3 is part of the restart library developed in this book and described in Appendix B. The restart library includes wrapper functions for many standard library functions that should be restarted if interrupted by a signal. Each function name starts with r_ followed by the name of the function. Include the restart.h header file when you use functions from the restart library in your programs.

Program 3.3 r_wait.c

A function that restarts wait if interrupted by a signal.

 #include <errno.h> #include <sys/wait.h> pid_t r_wait(int *stat_loc) {    int retval;    while (((retval = wait(stat_loc)) == -1) && (errno == EINTR)) ;    return retval; } 
Example 3.13

The following code segment waits for all children that have finished but avoids blocking if there are no children whose status is available. It restarts waitpid if that function is interrupted by a signal or if it successfully waited for a child.

 pid_t childpid; while (childpid = waitpid(-1, NULL, WNOHANG))    if ((childpid == -1) && (errno != EINTR))       break; 
Exercise 3.14

What happens when a process terminates, but its parent does not wait for it?

Answer:

It becomes a zombie in UNIX terminology. Zombies stay in the system until they are waited for. If a parent terminates without waiting for a child, the child becomes an orphan and is adopted by a special system process. Traditionally, this process is called init and has process ID equal to 1, but POSIX does not require this designation. The init process periodically waits for children, so eventually orphaned zombies are removed.

Example 3.15 fanwait.c

The following modification of the process fan of Program 3.2 causes the original process to print out its information after all children have exited.

 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> #include "restart.h" int main(int argc, char *argv[]) {    pid_t childpid;    int i, n;    if (argc != 2) {       fprintf(stderr, "Usage: %s n\n", argv[0]);       return 1;    }    n = atoi(argv[1]);    for (i = 1; i < n; i++)       if ((childpid = fork()) <= 0)          break;    while(r_wait(NULL) > 0) ; /* wait for all of your children */    fprintf(stderr, "i:%d  process ID:%ld  parent ID:%ld  child ID:%ld\n",            i, (long)getpid(), (long)getppid(), (long)childpid);    return 0; } 
Exercise 3.16

What happens if you interchange the while loop and fprintf statements in Example 3.15?

Answer:

The original process still exits last, but it may output its ID information before some of its children output theirs.

Exercise 3.17

What happens if you replace the while loop of Example 3.15 with the statement wait(NULL); ?

Answer:

The parent waits for at most one process. If a signal happens to come in before the first child completes, the parent won't actually wait for any children.

Exercise 3.18 parentwaitpid.c

Describe the possible forms of the output from the following program.

 #include <stdio.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main (void) {    pid_t childpid;                           /* set up signal handlers here ... */    childpid = fork();    if (childpid == -1) {       perror("Failed to fork");       return 1;    }    if (childpid == 0)       fprintf(stderr, "I am child %ld\n", (long)getpid());    else if (wait(NULL) != childpid)       fprintf(stderr, "A signal must have interrupted the wait!\n");    else       fprintf(stderr, "I am parent %ld with child %ld\n", (long)getpid(),            (long)childpid);    return 0; } 

Answer:

The output can have several forms, depending on exact timing and errors.

  1. If fork fails ( unlikely unless some other program has generated a runaway tree of processes and exceeded the system limit), the "Failed to fork" message appears. Otherwise, if there are no signals, something similar to the following appears.

     I am child 3427 I am parent 3426 with child 3427 
  2. If the parent catches a signal after the child executes fprintf but before the child's return , the following appears.

     I am child 3427 A signal must have interrupted the wait! 
  3. If the parent catches a signal after the child terminates and wait returns successfully, the following appears.

     I am child 3427 I am parent 3426 with child 3427 
  4. If the parent catches a signal between the time that the child terminates and wait returns, either of the previous two results is possible, depending on when the signal is caught.

  5. If the parent catches a signal before the child executes fprintf and if the parent executes its fprintf first, the following appears.

     A signal must have interrupted the wait! I am child 3427 
  6. Finally, if the parent catches a signal before the child executes fprintf and the child executes its fprintf first, the following appears.

     I am child 3427 A signal must have interrupted the wait! 
Exercise 3.19

For the child of Exercise 3.18 to always print its message first, the parent must run wait repeatedly until the child exits before printing its own message. What is wrong with the following?

 while(childpid != wait(&status)) ; 

Answer:

The loop fixes the problem of interruption by signals, but wait can fail to return the childpid because it encounters a real error. You should always test errno as demonstrated in the r_wait of Program 3.3.

Exercise 3.20 fanwaitmsg.c

The following program creates a process fan. All the forked processes are children of the original process. How are the output messages ordered?

 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main (int argc, char *argv[]) {    pid_t childpid = 0;    int i, n;    if (argc != 2){      /* check number of command-line arguments */       fprintf(stderr, "Usage: %s processes\n", argv[0]);       return 1;    }    n = atoi(argv[1]);    for (i = 1; i < n; i++)       if ((childpid = fork()) <= 0)          break;    for( ; ; ) {       childpid = wait(NULL);       if ((childpid == -1) && (errno != EINTR))         break;    }    fprintf(stderr, "I am process %ld, my parent is %ld\n",                    (long)getpid(), (long)getppid());    return 0; } 

Answer:

Because none of the forked children are parents, their wait function returns “1 and sets errno to ECHILD . They are not blocked by the second for loop. Their identification messages may appear in any order. The message from the original process comes out at the very end after it has waited for all of its children.

Exercise 3.21 chainwaitmsg.c

The following program creates a process chain. Only one forked process is a child of the original process. How are the output messages ordered?

 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h> int main (int argc, char *argv[]) {    pid_t childpid;    int i, n;    pid_t waitreturn;    if (argc != 2){   /* check number of command-line arguments */       fprintf(stderr, "Usage: %s processes\n", argv[0]);       return 1;    }    n = atoi(argv[1]);    for (i = 1; i < n; i++)       if (childpid = fork())          break;    while(childpid != (waitreturn = wait(NULL)))       if ((waitreturn == -1) && (errno != EINTR))          break;    fprintf(stderr, "I am process %ld, my parent is %ld\n",                      (long)getpid(), (long)getppid());    return 0; } 

Answer:

Each forked child waits for its own child to complete before outputting a message. The messages appear in reverse order of creation.

3.4.1 Status values

The stat_loc argument of wait or waitpid is a pointer to an integer variable. If it is not NULL , these functions store the return status of the child in this location. The child returns its status by calling exit , _exit , _Exit or return from main. A zero return value indicates EXIT_SUCCESS; any other value indicates EXIT_FAILURE . The parent can only access the 8 least significant bits of the child's return status.

POSIX specifies six macros for testing the child's return status. Each takes the status value returned by a child to wait or waitpid as a parameter.

  SYNOPSIS  #include <sys/wait.h>    WIFEXITED(int stat_val)    WEXITSTATUS(int stat_val)    WIFSIGNALED(int stat_val)    WTERMSIG(int stat_val)    WIFSTOPPED(int stat_val)    WSTOPSIG(int stat_val)  POSIX  

The six macros are designed to be used in pairs. The WIFEXITED evaluates to a nonzero value when the child terminates normally. If WIFEXITED evaluates to a nonzero value, then WEXITSTATUS evaluates to the low-order 8 bits returned by the child through _exit() , exit() or return from main .

The WIFSIGNALED evaluates to a nonzero value when the child terminates because of an uncaught signal (see Chapter 8). If WIFSIGNALED evaluates to a nonzero value, then WTERMSIG evaluates to the number of the signal that caused the termination.

The WIFSTOPPED evaluates to a nonzero value if a child is currently stopped. If WIFSTOPPED evaluates to a nonzero value, then WSTOPSIG evaluates to the number of the signal that caused the child process to stop.

Example 3.22 showreturnstatus.c

The following function determines the exit status of a child.

 #include <errno.h> #include <stdio.h> #include <sys/types.h> #include <sys/wait.h> #include "restart.h" void show_return_status(void) {    pid_t childpid;    int status;    childpid = r_wait(&status);    if (childpid == -1)       perror("Failed to wait for child");    else if (WIFEXITED(status) && !WEXITSTATUS(status))       printf("Child %ld terminated normally\n", (long)childpid);    else if (WIFEXITED(status))       printf("Child %ld terminated with return status %d\n",              (long)childpid, WEXITSTATUS(status));    else if (WIFSIGNALED(status))       printf("Child %ld terminated due to uncaught signal %d\n",              (long)childpid, WTERMSIG(status));    else if (WIFSTOPPED(status))       printf("Child %ld stopped due to signal %d\n",              (long)childpid, WSTOPSIG(status)); } 
Team-FLY


Unix Systems Programming
UNIX Systems Programming: Communication, Concurrency and Threads
ISBN: 0130424110
EAN: 2147483647
Year: 2003
Pages: 274

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net