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.