9.8. Signal handling
Two functions allow for asynchronous event handling to be provided.
A signal is a condition that may be reported during program
execution, and can be ignored, handled specially, or, as is the default,
used to terminate the program. One function sends signals, another is used
to determine how a signal will be processed. Many of the signals may be
generated by the underlying hardware or operating system as well as by means
of the signal-sending function raise
.
The signals are defined in the include file
<signal.h>
.
SIGABRT
- Abnormal termination, such as instigated by the
abort
function. (Abort.) SIGFPE
- Erroneous arithmetic operation, such as divide by 0 or overflow. (Floating point exception.)
SIGILL
- An ‘invalid object program’ has been detected. This usually means that there is an illegal instruction in the program. (Illegal instruction.)
SIGINT
- Interactive attention signal; on interactive systems this is usually generated by typing some ‘break-in’ key at the terminal. (Interrupt.)
SIGSEGV
- Invalid storage access; most frequently caused by attempting to store some value in an object pointed to by a bad pointer. (Segment violation.)
SIGTERM
- Termination request made to the program. (Terminate.)
Some implementations may have additional signals available, over and above
this standard set. They will be given names that start SIG
, and
will have unique values, apart from the set above.
The function signal
allows you to specify the action taken on
receipt of a signal. Associated with each signal condition above, there is
a pointer to a function provided to handle this signal. The signal function
changes this pointer, and returns the original value. Thus the function is
defined as
#include <signal.h> void (*signal (int sig, void (*func)(int)))(int);
That is to say, signal
is a function that returns a pointer
to another function. This second function takes a single int argument and
returns void
. The second argument to signal
is
similarly a pointer to a function returning void
which takes an
int
argument.
Two special values may be used as the func
argument (the
signal-handling function), SIG_DFL
, the initial, default,
signal handler; and SIG_IGN
, which is used to ignore
a signal. The implementation sets the state of all signals to one or other
of these values at the start of the program.
If the call to signal
succeeds, the previous value of
func
for the specified signal is returned. Otherwise,
SIG_ERR
is returned and errno
is set.
When a signal event happens which is not being ignored, if the
associated func is a pointer to a function, first the equivalent of
signal(sig, SIG_DFL)
is executed. This resets the signal
handler to the default action, which is to terminate the program. If
the signal was SIGILL
then this resetting is implementation
defined. Implementations may choose to ‘block’ further instances of
the signal instead of doing the resetting.
Next, a call is made to the signal-handling function. If that
function returns normally, then under most circumstances the
program will resume at the point where the event occurred. However, if the
value of sig
was SIGFPE
(a floating point
exception), or any implementation defined computational exception,
then the behaviour is undefined. The most usual thing to do in the handler
for SIGFPE
is to call one of the functions
abort
, exit
, or longjmp
.
The following program fragment shows the use of signal to perform a tidy exit to a program on receipt of the interrupt or ‘interactive attention’ signal.
#include <stdio.h> #include <stdlib.h> #include <signal.h> FILE *temp_file; void leave(int sig); main() { (void) signal(SIGINT,leave); temp_file = fopen("tmp","w"); for(;;) { /* * Do things.... */ printf("Ready...\n"); (void)getchar(); } /* can't get here ... */ exit(EXIT_SUCCESS); } /* * on receipt of SIGINT, close tmp file * but beware - calling library functions from a * signal handler is not guaranteed to work in all * implementations..... * this is not a strictly conforming program */ void leave(int sig) { fprintf(temp_file,"\nInterrupted..\n"); fclose(temp_file); exit(sig); }Example 9.4
It is possible for a program to send signals to itself by means of the
raise
function. This is defined as follows
include <signal.h> int raise (int sig);
The signal sig is sent to the program.
Raise
returns zero if successful, non-zero otherwise. The
abort
library function is essentially implementable as
follows:
#include <signal.h> void abort(void) { raise(SIGABRT); }
If a signal occurs for any reason other than calling abort or raise,
the signal-handling function may only call signal or assign a value to
a volatile static object of type sig_atomic_t
. The type
sig_atomic_t
is declared in <signal.h>
.
It is the only type of object that can safely be modified as an atomic
entity, even in the presence of asynchronous interrupts. This is a very
onerous restriction imposed by the Standard, which, for example, invalidates
the leave
function in the example program above; although
the function would work correctly in some environments, it does not follow
the strict rules of the Standard.