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.