Team-FLY |
3.4 The wait FunctionWhen 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 .
Example 3.12The 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.cA 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.13The 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.14What 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.cThe 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.16What 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.17What 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.cDescribe 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.
Exercise 3.19For 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.cThe 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.cThe 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 valuesThe 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.cThe 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 |