Noeud:Signals, Noeud « Next »:, Noeud « Up »:Input and Output



Signals

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:

Ignore
It ignores the signal and keeps running as if nothing happened.
Terminate
It terminates, possibly leaving a core dump.
Stop
It stops running, in a way that allows the program to be restarted.
Continue
If the program is currently stopped, then it resumes running.
Execute handler
It executes some signal-handling routine previously installed by the program. (This action is never the default.)

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 sigaction1 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
Restore the handling of this signal to the default (as shown in the earlier table).
SIG_IGN
Ignore any instances of this signal.
handler
When this signal arrives, execute the named handler function, which must have a prototype like this:

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.


Notes de bas de page

  1. This function replaces signal, an older library call which is best avoided altogether since its behaviour varies between systems.