A signal is a software notification to a process of an event.
Terminology:
Signal | Description | default action |
SIGABRT | process abort | implementation dependent |
SIGALRM | alarm clock | abnormal termination |
SIGBUS | access undefined part of memory object | implementation dependent |
SIGCHLD | child terminated, stopped or continued | ignore |
SIGCONT | execution continued if stopped | continue |
SIGFPE | error in arithmetic operation as in division by zero | implementation dependent |
SIGHUP | hang-up (death) on controlling terminal (process) | abnormal termination |
SIGILL | invalid hardware instruction | implementation dependent |
SIGINT | interactive attention signal (usually ctrl-C) | abnormal termination |
SIGKILL | terminated (cannot be caught or ignored) | abnormal termination |
SIGPIPE | write on a pipe with no readers | abnormal termination |
SIGQUIT | interactive termination: core dump (usually ctrl-|) | implementation dependent |
SIGSEGV | invalid memory reference | implementation dependent |
SIGSTOP | execution stopped (cannot be caught or ignored) | stop |
SIGTERM | termination | abnormal termination |
SIGTSTP | terminal stop | stop |
SIGTTIN | background process attempting to read | stop |
SIGTTOU | background process attempting to write | stop |
SIGURG | high bandwidth data available at a socket | ignore |
SIGUSR1 | user-defined signal 1 | abnormal termination |
SIGUSR2 | user-defined signal 2 | abnormal termination |
You can send a signal to a process from the command line using kill
kill -l will list the signals the system understands
kill [-signal] pid will send a signal to a process.
The optional argument may be a name or a number.
The default is SIGTERM.
To unconditionally kill a process, use:
kill -9 pid which is
kill -KILL pid.
From a program you can use the kill system call:
#include <sys/types.h> #include <signal.h> int kill(pid_t pid, int sig);Example 8.4: send SIGUSR1 to process 3423:
if (kill(3423, SIGUSR1) == -1) perror("Failed to send the SIGUSR1 signal");Example 8.5: a child kills its parent:
if (kill(getppid(), SIGTERM) == -1) perror ("Failed to kill parent");Example 8.5: a process sends a signal to itself:
if (raise(SIGUSR1) != 0) perror("Failed to raise SIGUSR1");Example 8.8: kill an infinite loop after 10 seconds:
int main(void) { alarm(10); for ( ; ; ) ; }
Signal Mask and Signal Sets
How do you deal with a set of signals?
Originally, an int would hold a collection of bits, one per signal.
Now, signal sets of type sigset_t are used.
Here are the routines for handling sets of signals.
You should compare these to the way select handles sets of file descriptors.
#include <signal.h> int sigemptyset(sigset_t *set); int sigfillset(sigset_t *set); int sigaddset(sigset_t *set, int signo); int sigdelset(sigset_t *set, int signo); int sigismember(const sigset_t *set, int signo);sigemptyset initializes the set to contain no signals sigfillset puts all signals in the set
The process signal mask is a set of signals that are blocked.
A blocked signal remains pending after it is generated until the signal
is unblocked.
The process signal mask is modified with the sigprocmask system call.
#include <signal.h> int sigprocmask(int how, const sigset_t *restrict set, sigset_t *restrict oset);
The how can be:
Either set or oset may be NULL.
Example 8.9: initialize a signal set:
if ((sigemptyset(&twosigs) == -1) || (sigaddset(&twosigs, SIGINT) == -1) || (sigaddset(&twosigs, SIGQUIT) == -1)) perror("Failed to set up signal mask");Exampel 8.10: add SIGINT to the set of blocked signals:
sigset_t newsigset; if ((sigemptyset(&newsigset) == -1) || (sigaddset(&newsigset, SIGINT) == -1)) perror("Failed to initialize the signal set"); else if (sigprocmask(SIG_BLOCK, &newsigset, NULL) == -1) perror("Failed to block SIGINT");Program 8.1 on page 263 blocks and unblocks SIGINT
Program 8.2 on page 264 blocks a signal while creating two pipes.
Program 8.3 on page 265 blocks all signals before a fork and exec.
Program 8.4 on page 266 blocks some signals while getting a password.
The process signal mask is inherited by the child process.
Catching and Ignoring Signals: sigaction
#include <signal.h> int sigaction(int signo, const struct sigaction *act, struct sigaction *oact); struct sigaction { void (*sa_handler)(int); /* SIG_DFL, SIG_IGN or pointer to function */ sigset_t sa_mask; /* additional signals to be blocked during execution of handler */ int sa_flags; /* special flags and options */ void(*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */ };Either act or oact may be NULL.
If the SA_SIGINFO flag of the sa_flags field is clear, sa_handler specifies the action to be taken.
void (*sa_handler)() means a pointer to a function that has not return value.
Example 8.15: ignore SIGINT if the default action is set:
struct sigaction act; if (sigaction(SIGINT, NULL, &act) == -1) /* Find current SIGINT handler */ perror("Failed to get old handler for SIGINT"); else if (act.sa_handler == SIG_DFL) { /* if SIGINT handler is default */ act.sa_handler = SIG_IGN; /* set new SIGINT handler to ignore */ if (sigaction(SIGINT, &act, NULL) == -1) perror("Failed to ignore SIGINT"); }
Example 8.16: set up a signal handler for SIGINT:
void catchctrlc(int signo) { char handmsg[] = "I found Ctrl-C\n"; int msglen = sizeof(handmsg); write(STDERR_FILENO, handmsg, msglen); } ... struct sigaction act; act.sa_handler = catchctrlc; act.sa_flags = 0; if ((sigemptyset(&act.sa_mask) == -1) || (sigaction(SIGINT, &act, NULL) == -1)) perror("Failed to set SIGINT to handle Ctrl-C");
Example 8.18: set the action of SIGINT to the default:
struct sigaction newact; newact.sa_handler = SIG_DFL; /* new handler set to default */ newact.sa_flags = 0; /* no special options */ if ((sigemptyset(&newact.sa_mask) == -1) || (sigaction(SIGINT, &newact, NULL) == -1)) perror("Failed to set SIGINT to the default action");
Example 8.19: see if a signal is ignored:
int testignored(int signo) { struct sigaction act; if ((sigaction(signo, NULL, &act) == -1) || (act.sa_handler != SIG_IGN)) return 0; return 1; }
Program 8.5: A program that terminates gracefully on ctrl-C
Program 8.6: A program to estimate the average value of sin(x)
Waiting for signals
Old way: pause
#include <unistd.h> int pause(void);Exercise 8.21: an incorrect way to wait for a signal:
static volatile sig_atomic_t sigreceived = 0; while(sigreceived == 0) pause();Exercise 8.22: another incorrect way to wait for a signal:
static volatile sig_atomic_t sigreceived = 0; int signum; sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, signum); sigprocmask(SIG_BLOCK, &sigset, NULL); while(sigreceived == 0) pause();What you need to do: atomically unblock the signal and suspend the process.
#include <signal.h> int sigsuspend(const sigset_t *sigmask);Sets the signal mask to the one pointed to by sigmask and suspends the process until a signal is received.
Example 8.25: a correct way to wait for a signal:
1. static volatile sig_atomic_t sigreceived = 0; 3. sigset_t maskblocked, maskold, maskunblocked; 4. int signum = SIGUSR1; 6. sigprocmask(SIG_SETMASK, NULL, &maskblocked); 7. sigprocmask(SIG_SETMASK, NULL, &maskunblocked); 8. sigaddset(&maskblocked, signum); 9. sigdelset(&maskunblocked, signum); 10. sigprocmask(SIG_BLOCK, &maskblocked, &maskold); 11. while(sigreceived == 0) 12. sigsuspend(&maskunblocked); 13. sigprocmask(SIG_SETMASK, &maskold, NULL);
Program 8.7, simplesuspend, shows how to safely block on a specific signal.
Program 8.8 shows how to use Program 8.7.
Programs 8.9 and 8.10 implement a simple biff.
sigwait
This provides an alternative way to wait for signals.
Idea:
#include <signal.h> int sigwait(const sigset_t *restrict sigmask, int *restrict signo);Look at Program 8.11 on page 283 that counts signals.
Errors and async-signal safety
Three issues in handling signals:
The restart library handles this for many of the most important system calls.
Look at the functions in the restart library.
void myhandler(int signo) { int esaved; esaved = errno; write(STDOUT_FILENO, "Got a signal\n", 13); errno = esaved; }
_Exit | execve | lseek | sendto | stat |
_exit | fchown | lstat | setgid | symlink |
accept | fcntl | mkdir | setpgid | sysconf |
access | fdatasync | mkfifo | setsid | tcdrain |
aio_error | fork | open | setsockopt | tcflow |
aio_return | fpathconf | pathconf | setuid | tcflush |
aio_suspend | fstat | pause | shutdown | tcgetattr |
alarm | fsync | pipe | sigaction | tcgetpgrp |
bind | ftruncate | poll | sigaddset | tcsendbreak |
cfgetispeed | getegid | posix_trace_event | sigdelset | tcsetattr |
cfgetospeed | geteuid | pselect | sigemptyset | tcsetpgrp |
cfsetispeed | getgid | raise | sigfillset | time |
cfsetospeed | getgroups | read | sigismember | timer_getoverrun |
chdir | getpeername | readlink | signal | timer_gettime |
chmod | getpgrp | recv | sigpause | timer_settime |
chown | getpid | recvfrom | sigpending | times |
clock_gettime | getppid | recvmsg | sigprocmask | umask |
close | getsockname | rename | sigqueue | uname |
connect | getsockopt | rmdir | sigset | unlink |
creat | getuid | select | sigsuspend | utime |
dup | kill | sem_post | sleep | wait |
dup2 | link | send | socket | waitpid |
execle | listen | sendmsg | socketpair | write |
Realtime Signals: From Section 9.4
Recall the sigaction structure:
struct sigaction { void (*sa_handler)(int); /* SIG_DFL, SIG_IGN or pointer to function */ sigset_t sa_mask; /* additional signals to be blocked during execution of handler */ int sa_flags; /* special flags and options */ void(*sa_sigaction) (int, siginfo_t *, void *); /* realtime handler */ };If the SA_SIGINFO flag of the sa_flags field is set, sa_sigaction specifies the action to be taken.
This defines a new type a signal handler that takes three parameters instead
of one.
It also defines a new data type: siginfo_t which is a structure
containing at least the following:
int si_signo; /* signal number */ int si_code; /* cause of the signal */ union sigval si_value; /* signal value */where the union sigval contains at least:
int sival_int; void *sival_ptr;
Signals set up with this type of signal handler are queued.
The system call to send one of these signals is called sigqueue
rather than kill:
#include <signal.h> int sigqueue(pid_t pid, int signo, const union sigval value);
On some systems, only certain signals can be queued, those between SIGRTMIN and SIGRTMAX.
Program 9.9, page 323 shows a program to send a queued signal to a process. Note that there is no command line function similar to kill.
Program 9.10, page 324, gives an example program that can receive SIGUSR1 signals.