Linux - Exit and Wait
Linux - Exit and Wait
• 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);
• C standard specifies two constants, EXIT_SUCCESS (0) and EXIT_FAILURE (1). These are
macros defined in stdlib.h. The following actions are performed by exit():
• Exit handlers (functions registered with atexit() and on_exit()) are called, in reverse order
of their registration
• The stdio stream buffers are flushed.
• The _exit() system call is invoked, using the value supplied in status.
• 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). But return is a control statement and
exit() is a function
atexit()…a library function
• Sometimes, an application needs to automatically perform some operations on process
termination such as garbage collection. These tasks are performed by exit handlers. An
exit handler is a programmer-supplied function that is registered at some point during
the life of the process and is then automatically called during normal process termination
via exit(). Exit handlers are not called if a program calls _exit() directly or if the process is
terminated abnormally by a signal.
• Registering exit handlers: The GNU C library provides two ways of registering exit
handlers. Using atexit() or onexit().
#include <stdlib.h>
int atexit(void (*func)(void));
• Returns 0 on success, or nonzero on error
• The atexit() function adds func to a list of functions that are called when the process
terminates. The function func should be defined to take no arguments and return no value,
thus having the following general form:
void func(void)
{
/* Perform some actions */
}
• Note that atexit() returns a nonzero value (not necessarily –1) on error.
• It is possible to register multiple exit handlers (and even the same exit handler multiple
times).
• When the program invokes exit(), these functions are called in reverse order of registration.
• This ordering is logical because, typically, functions that are registered earlier are those
that carry out more fundamental types of cleanups that may need to be performed after
later-registered functions.
• A child process created via fork() inherits a copy of its parent’s exit handler registrations.
example main()
{
#include<stdio.h> atexit(abc); // registering the functions
#include<stdlib.h> atexit(def);
void abc(void) printf("Hello...\n");
sleep(5);
{ exit(0); // call all registered functions in reverse order
printf("In abc...\n"); // before terminating the process
} }
void def(void)
{ $ ./a.out
printf("In def...\n"); Hello...
} In def...
In abc...
exit() _exit()
Library function System call
For normal process termination For immediate process termination
Calls registered functions
Does not call registered functions (exit handlers)
void abc(void)
{ void abc(void)
printf("In abc...\n"); {
} printf("In abc...\n");
}
main()
{ main()
atexit(abc); {
printf("Hello...\n"); atexit(abc);
sleep(1); printf("Hello...\n");
exit(0); sleep(1);
_exit(0);
}
}
Output:
Hello... Output:
In abc... Hello...
Flushes out and closes stdout buffer Does not flush out and close stdout buffer
main() main()
{ {
printf("Hello..."); printf("Hello...");
sleep(1); sleep(1);
exit(0); _exit(0);
} }
Output: Output:
Hello...
wait()… a system call
• In many application designs, a parent process needs to know when one of its child
processes changes state—when the child terminates or is stopped by a signal.
• The use of wait() or waitpid() helps parent process in monitoring its child processes.
• The wait() system call waits for one of the children of the calling process to terminate
and returns the termination status of that child in the buffer pointed to by status.
• #include <sys/wait.h>
• pid_t wait(int *status);
• Returns process ID of terminated child, or –1 on error
• Advantages of using wait in parent process
• It collects exit status of child thus removing it from zombie.
• It blocks parent till child completes thus avoids child becoming orphan.
waitpid()… a system call
• The wait() system call has a number of limitations, which waitpid() was designed to
address:
• If a parent process has created multiple children, it is not possible to wait() for the
completion of a specific child; you can only wait for the next child that terminates.
• If no child has yet terminated, wait() always blocks. Sometimes, it would be preferable to
perform a non blocking wait so that if no child has yet terminated, you obtain an
immediate indication of this fact.
• Using wait(), you can find out only about children that have terminated. It is not possible
to be notified when a child is stopped by a signal (for eg, signal 19) or when a stopped
child is resumed by delivery of a signal number 18.
• #include <sys/wait.h>
• pid_t waitpid(pid_t pid, int *status, int options);
• Returns process ID of child, 0 (see text), or –1 on error
• The return value and status arguments of waitpid() are the same as for wait().
• The pid argument enables the selection of the child to be waited for, as follows:
• If pid is greater than 0, wait for the child whose process ID equals pid.
• If pid equals –1, wait for any child. The call wait(&status) is equivalent to the call waitpid(–1, &status, 0).
• The options argument is a bit mask that can include (OR) zero or more of the following flags
• WUNTRACED: In addition to returning information about terminated children, also return information
when a child is stopped by a signal.
• WCONTINUED: Also return status information about stopped children that have been resumed by delivery
of a signal number 18.
• WNOHANG: If no child specified by pid has yet changed state, then return immediately, instead of blocking.
In this case, the return value of waitpid() is 0. If the calling process has no children that match the
specification in pid, waitpid() fails with the error ECHILD. On failure waitpid() returns -1.
The Wait Status Value
• The status value returned by wait() and waitpid() allows us to distinguish the following
events for the child:
• The child terminated by calling _exit() (or exit()), specifying an integer exit status.
• The child was terminated by the delivery of an unhandled signal.
• The child was stopped by a signal, and waitpid() was called with the WUNTRACED flag.
• The child was resumed by a SIGCONT signal, and waitpid() was called with the
WCONTINUED flag.
• Although defined as an int, only the bottom 2 bytes of the value pointed to by status are
actually used. The way in which these 2 bytes are filled depends on which of the above
events occurred for the child. The following layout of the wait status value is for
Linux/x86-32. The details vary across implementations.
• Portable applications should always use the following
macros to inspect this value, rather than directly
inspecting its bit-mask components.
• The <sys/wait.h> header file defines a standard set of
macros that can be used to dissect a wait status value.
• When applied to a status value returned by wait() or
waitpid(), only one of the macros in the list below will
return true. Additional macros are provided to further
dissect the status value, as noted in the list
• WIFEXITED(status): This macro returns true if the child process exited normally. In this case, the
macro WEXITSTATUS(status) returns the exit status of the child process.
• WIFSIGNALED(status): This macro returns true if the child process was killed by a signal. In this case,
the macro WTERMSIG(status) returns the number of the signal that caused the process to terminate,
and the macro WCOREDUMP(status) returns true if the child process produced a core dump file.
• WIFSTOPPED(status): This macro returns true if the child process was stopped by a signal. In this
case, the macro WSTOPSIG(status) returns the number of the signal that stopped the process.
• WIFCONTINUED(status): This macro returns true if the child was resumed by delivery of SIGCONT
(signal 18).