Program Termination

The usual way for a program to terminate is simply for its main function to return. The exit status value returned from the main function is used to report information back to the process's parent process or shell.

A program can also terminate normally by calling the exit function.

In addition, programs can be terminated by signals; this is discussed in more detail in Chapter 25. The abort function causes a signal that kills the program.

Normal Termination

A process terminates normally when its program signals it is done by calling exit. Returning from main is equivalent to calling exit, and the value that main returns is used as the argument to exit.

void function>exit/function> (int status) The exit function tells the system that the program is done, which causes it to terminate the process.

status is the program's exit status, which becomes part of the process' termination status. This function does not return.

Normal termination causes the following actions:

  1. Functions that were registered with the atexit or on_exit functions are called in the reverse order of their registration. This mechanism allows your application to specify its own "cleanup" actions to be performed at program termination. Typically, this is used to do things like saving program state information in a file, or unlocking locks in shared data bases.

  2. All open streams are closed, writing out any buffered output data. See the section called “Closing Streams”. In addition, temporary files opened with the tmpfile function are removed; see the section called “Temporary Files”.

  3. _exit is called, terminating the program. the section called “Termination Internals”.

Exit Status

When a program exits, it can return to the parent process a small amount of information about the cause of termination, using the exit status. This is a value between 0 and 255 that the exiting process passes as an argument to exit.

Normally you should use the exit status to report very broad information about success or failure. You can't provide a lot of detail about the reasons for the failure, and most parent processes would not want much detail anyway.

There are conventions for what sorts of status values certain programs should return. The most common convention is simply 0 for success and 1 for failure. Programs that perform comparison use a different convention: they use status 1 to indicate a mismatch, and status 2 to indicate an inability to compare. Your program should follow an existing convention if an existing convention makes sense for it.

A general convention reserves status values 128 and up for special purposes. In particular, the value 128 is used to indicate failure to execute another program in a subprocess. This convention is not universally obeyed, but it is a good idea to follow it in your programs.

Warning: Don't try to use the number of errors as the exit status. This is actually not very useful; a parent process would generally not care how many errors occurred. Worse than that, it does not work, because the status value is truncated to eight bits. Thus, if the program tried to report 256 errors, the parent would receive a report of 0 errors--that is, success.

For the same reason, it does not work to use the value of errno as the exit status--these can exceed 255.

Portability note: Some non-POSIX systems use different conventions for exit status values. For greater portability, you can use the macros EXIT_SUCCESS and EXIT_FAILURE for the conventional status value for success and failure, respectively. They are declared in the file stdlib.h. int function>EXIT_SUCCESS/function> This macro can be used with the exit function to indicate successful program completion.

On POSIX systems, the value of this macro is 0. On other systems, the value might be some other (possibly non-constant) integer expression.

int function>EXIT_FAILURE/function> This macro can be used with the exit function to indicate unsuccessful program completion in a general sense.

On POSIX systems, the value of this macro is 1. On other systems, the value might be some other (possibly non-constant) integer expression. Other nonzero status values also indicate failures. Certain programs use different nonzero status values to indicate particular kinds of "non-success". For example, diff uses status value 1 to mean that the files are different, and 2 or more to mean that there was difficulty in opening the files.

Don't confuse a program's exit status with a process' termination status. There are lots of ways a process can terminate besides having it's program finish. In the event that the process termination is caused by program termination (i.e. exit), though, the program's exit status becomes part of the process' termination status.

Cleanups on Exit

Your program can arrange to run its own cleanup functions if normal termination happens. If you are writing a library for use in various application programs, then it is unreliable to insist that all applications call the library's cleanup functions explicitly before exiting. It is much more robust to make the cleanup invisible to the application, by setting up a cleanup function in the library itself using atexit or on_exit.

int function>atexit/function> (void (*function) (void)) The atexit function registers the function function to be called at normal program termination. The function is called with no arguments.

The return value from atexit is zero on success and nonzero if the function cannot be registered.

int function>on_exit/function> (void (*function)(int status, void *arg), void *arg) This function is a somewhat more powerful variant of atexit. It accepts two arguments, a function function and an arbitrary pointer arg. At normal program termination, the function is called with two arguments: the status value passed to exit, and the arg.

This function is included in the GNU C library only for compatibility for SunOS, and may not be supported by other implementations.

Here's a trivial program that illustrates the use of exit and atexit:

#include stdio.h
#include stdlib.h

void
bye (void)
{
  puts ("Goodbye, cruel world....");
}

int
main (void)
{
  atexit (bye);
  exit (EXIT_SUCCESS);
}

When this program is executed, it just prints the message and exits.

Aborting a Program

You can abort your program using the abort function. The prototype for this function is in stdlib.h. void function>abort/function> (void) The abort function causes abnormal program termination. This does not execute cleanup functions registered with atexit or on_exit.

This function actually terminates the process by raising a SIGABRT signal, and your program can include a handler to intercept this signal; see Chapter 25.

Future Change Warning: Proposed Federal censorship regulations may prohibit us from giving you information about the possibility of calling this function. We would be required to say that this is not an acceptable way of terminating a program.

Termination Internals

The _exit function is the primitive used for process termination by exit. It is declared in the header file unistd.h. void function>_exit/function> (int status) The _exit function is the primitive for causing a process to terminate with status status. Calling this function does not execute cleanup functions registered with atexit or on_exit.

void function>_Exit/function> (int status) The _Exit function is the ISO C equivalent to _exit. The ISO C committee members were not sure whether the definitions of _exit and _Exit were compatible so they have not used the POSIX name.

This function was introduced in ISO C99 and is declared in stdlib.h.

When a process terminates for any reason--either because the program terminates, or as a result of a signal--the following things happen:

  • All open file descriptors in the process are closed. Chapter 14. Note that streams are not flushed automatically when the process terminates; see Chapter 13.

  • A process exit status is saved to be reported back to the parent process via wait or waitpid; see the section called “Process Completion”. If the program exited, this status includes as its low-order 8 bits the program exit status.

  • Any child processes of the process being terminated are assigned a new parent process. (On most systems, including GNU, this is the init process, with process ID 1.)

  • A SIGCHLD signal is sent to the parent process.

  • If the process is a session leader that has a controlling terminal, then a SIGHUP signal is sent to each process in the foreground job, and the controlling terminal is disassociated from that session. Chapter 28.

  • If termination of a process causes a process group to become orphaned, and any member of that process group is stopped, then a SIGHUP signal and a SIGCONT signal are sent to each process in the group. Chapter 28.