UNIX systems send signals to programs to indicate program faults, user-requested interrupts, and other situations. More generally, signals are a simple interprocess communication - an aspect that's taken much further with the extended signal facilities that POSIX.4 defines. For the most part, signals are simply the way in which the operating system informs the process of an action that requires attention - whether it's a memory access violation or an external event. However, processes can also signal each other when that is useful.
Many signals are purely informative; others cause the program to change
its state in some way. For example, when you enter Ctrl C at the
keyboard, your shell will send the signal SIGINT
to the
foreground process, which asks the program to terminate. Or, if the
program attempts an illegal memory access, the operating system sends it
the signal SIGSEGV
.
When a signal arrives, the program takes one of the following actions:
Each signal has a default action, which determines what effect the
signal has when it arrives, but a program can install a non-standard
action for most signals which then overrides the default action. The
signals on any system are defined in the header file signal.h
.
They vary from system to system, though most UNIX systems have some
superset of 32 standard signals. The following important signals are
almost always available:
Signal | Default Action | Meaning
|
SIGHUP | Terminate | Hangup; sent when the system asks a program to cleanly exit
|
SIGINT | Terminate | Interrupt; often sent when user types Ctrl C
|
SIGQUIT | Terminate | Quit; often sent when a user types Ctrl \
|
SIGILL | Terminate | Illegal instruction
|
SIGTRAP | Terminate | Sent when a program reaches a previously set breakpoint
|
SIGABRT | Terminate | Sent when the program calls abort
|
SIGFPE | Terminate | Floating point arithmetic error
|
SIGKILL | Terminate | Enforced program termination
|
SIGUSR1 | Terminate | User defined signal 1
|
SIGUSR2 | Terminate | User defined signal 2
|
SIGSEGV | Terminate | Segmentation violation; illegal memory access
|
SIGPIPE | Terminate | Broken command pipe
|
SIGALRM | Terminate | Alarm clock signal
|
SIGTERM | Terminate | Termination request (sent by software)
|
SIGCHLD | Terminate | Child process has stopped or terminated
|
SIGCONT | Continue | Continue processing if stopped
|
SIGSTOP | Stop | Stop processing; sent by the system
|
SIGTSTP | Stop | Stop processing; often sent when a user types Ctrl Z
|
SIGTTIN | Stop | Background program requires input
|
SIGTTOU | Stop | Background program cannot output
|
Of the signals listed above, SIGKILL
and SIGSTOP
are
special: they are unblockable. That is, a program cannot change
the handling of these signals to be different from the default. When a
program receives a SIGKILL
, it is always stopped dead in its
tracks; similarly a SIGSTOP
always causes the program to stop
processing (and wait for a SIGCONT
to wake it up again.
It's also worth noting that SIGUSR1
and SIGUSR2
are
provided specifically as user-definable signals for your applications.
These signals have no predefined default meaning, and are never sent to
a process by the operating system.
There are functions for dealing with signals: raise
, which a
program can use to send a signal to itself, kill
for sending a
signal to an arbitrary process, and sigaction
1 that a program uses to change the action for any signal.
raise
is very simple; it has a single argument, which is
the name of a signal (i.e., one of the constants defined above). Here's
how it is used:
#include <signal.h> int ret; ... ret = raise (SIGINT);
Example 2.7: Using the raise
library call.
This function call sends the signal SIGINT
. The return value,
ret
, is 0 if raise
is successful; -1 if it fails.
However, note that a signal may terminate the process - in which case,
raise
will never return.
If you want to send a signal to an arbitrary process (rather than
sending a signal to yourself), you need to call kill
, which has
two arguments: a process ID and a signal name. It works in much the
same way as the kill command that you can run from the command
line. The excerpt in example 2.8
behaves in exactly the same way as example 2.7 - the program sends a SIGINT
signal to itself.
#include <signal.h> #include <sys/types.h> /* For pid_t definition. */ int ret; ... ret = kill (getpid (), SIGINT);
Example 2.8: Using the kill
system call.
As with raise
, when kill
returns, ret
will be 0 if
the call was successful, or -1 if it failed. Of course, with this
example, if there is no handler installed for SIGINT
, then the
default action is to terminate the process, so kill
won't
actually return at all.
The function sigaction
changes the action that a process takes
when it receives a signal. It uses a structure to pass information
about the handling of a signal:
struct sigaction { void (*sa_handler)(int); void (*sa_sigaction)(int, siginfo_t *, void *); sigset_t sa_mask; int sa_flags; }
Under most conditions, the sa_handler
field is used to describe
the action to be taken on receipt of a signal. Finer control can be
gained by setting the SA_SIGINFO
bit of sa_flags
and then
using the sa_sigaction
field instead of sa_handler
to
describe what action to take when the signal arrives. We will describe
how to use sa_handler
style signal handling here: Check your
system manual pages, or the glibc manual for details of how to use
sa_sigaction
.
You must set sa_handler
to one of the following values:
SIG_DFL
SIG_IGN
handler
void handler_function (int signum) | signal handler |
The number of the signal that triggered the call to
handler_function will be passed as signum.
|
If the handler exits by calling return
, the program continues
executing after receiving the signal. If the handler calls exit
or abort
, execution doesn't continue after receiving the signal.
While executing the action for a signal, the arrival of subsequent
signals with the same number is blocked - unless the SA_NOMASK
bit of sa_flags is set to prevent that behaviour. Additional
different signal numbers can also be blocked during the execution of the
action by adding them to the sa_mask field of the struct
sigaction
. If you find you need to do this, your system manual page
for sigprocmask
explains how to manipulate sigset_t
types.
Here's a very simple program that exercises the signal handler:
#include <signal.h> #include <string.h> void sighandler (int signal) { printf ("received signal %d\n", signal); abort (); } int main (int argc, const char *argv[]) { struct sigaction action; memset (&action, 0, sizeof (struct sigaction)); action.sa_handler = sighandler; if (sigaction (SIGINT, &action, NULL) != 0) perror ("sigaction"); sleep (60); }
Example 2.9: Setting a simple signal
handler.
The program installs the function sighandler
as an interrupt
handler for SIGINT
; then it sleeps, waiting for the user to type
CTRL-C. When you type CTRL-C, sighandler
is called;
it prints its message, then calls abort
to terminate execution.
Here's how it looks:
$ ./a.out CTRL-Creceived signal 2 Abort (core dumped) $
You can install a separate signal handler for each signal that you care about; or else, since the argument passed to the handler identifies the signal that triggered this call, you can write a single handler function and let it figure out what to do on the basis of that argument.