This chapter describes what happens when a process terminates. We begin by describing the use of exit() and _exit() to terminate a process. We then discuss the use of exit handlers to automatically perform cleanups when a process calls exit(). We conclude by considering some interactions between fork(), stdio buffers, and exit().
A process may terminate in two general ways. One of these is abnormal termination, caused by the delivery of a signal whose default action is to terminate the process (with or without a core dump), as described in Section 20.1. Alternatively, a process can terminate normally, using the _exit() system call.
#include <unistd.h>
void _exit
(int status);
The status argument given to _exit() defines the termination status of the process, which is available to the parent of this process when it calls wait(). Although defined as an int, only the bottom 8 bits of status are actually made available to the parent. By convention, a termination status of 0 indicates that a process completed successfully, and a nonzero status value indicates that the process terminated unsuccessfully. There are no fixed rules about how nonzero status values are to be interpreted; different applications follow their own conventions, which should be described in their documentation. SUSv3 specifies two constants, EXIT_SUCCESS
(0) and EXIT_FAILURE
(1), that are used in most programs in this book.
A process is always successfully terminated by _exit() (i.e., _exit() never returns).
Although any value in the range 0 to 255 can be passed to the parent via the status argument to _exit(), specifying values greater than 128 can cause confusion in shell scripts. The reason is that, when a command is terminated by a signal, the shell indicates this fact by setting the value of the variable $? to 128 plus the signal number, and this value is indistinguishable from that yielded when a process calls _exit() with the same status value.
Programs generally don’t call _exit() directly, but instead call the exit() library function, which performs various actions before calling _exit().
#include <stdlib.h>
void exit
(int status);
The following actions are performed by exit():
Exit handlers (functions registered with atexit() and on_exit()) are called, in reverse order of their registration (Exit Handlers).
The stdio stream buffers are flushed.
The _exit() system call is invoked, using the value supplied in status.
Unlike _exit(), which is UNIX-specific, exit() is defined as part of the standard C library; that is, it is available with every C implementation.
One other way in which a process may terminate is to return from main(), either explicitly, or implicitly, by falling off the end of the main() function. Performing an explicit return n is generally equivalent to calling exit(n), since the run-time function that invokes main() uses the return value from main() in a call to exit().
There is one circumstance in which calling exit() and returning from main() are not equivalent. If any steps performed during exit processing access variables local to main(), then doing a return
from main() results in undefined behavior. For example, this could occur if a variable that is local to main() is specified in a call to setvbuf() or setbuf() (Buffering in the stdio Library).
Performing a return without specifying a value, or falling off the end of the main() function, also results in the caller of main() invoking exit(), but with results that vary depending on the version of the C standard supported and the compilation options employed:
In C89, the behavior in these circumstances is undefined; the program can terminate with an arbitrary status value. This is the behavior that occurs by default with gcc on Linux, where the exit status of the program is taken from some random value lying on the stack or in a particular CPU register. Terminating a program in this way should be avoided.
The C99 standard requires that falling off the end of the main program should be equivalent to calling exit(0). This is the behavior we obtain on Linux if we compile a program using gcc -std=c99.