On Unix systems, there are several ways to send signals to processes—with a kill command, with a keyboard sequence (like control-C), or through your own program (e.g., using a kill command in C). Signals are also generated by hardware exceptions such as segmentation faults and illegal instructions, timers and child process termination.
But how do you know what signals a process will react to? After all, what a process is programmed to do and able to ignore is another issue.
Fortunately, the /proc file system makes information about how processes handle signals (and which they block or ignore) accessible with commands like the one shown below. In this command, we’re looking at information related to the login shell for the current user, the "$$" representing the current process.
$ cat /proc/$$/status | grep Sig SigQ: 0/15432 SigPnd: 0000000000000000 SigBlk: 0000000000010000 SigIgn: 0000000000380004 SigCgt: 000000004b817efb
Interpreting the data is a little tricky, but not too much so. So, what does any of this mean? First, let’s look at the definitions of each of these fields.
- SigQ – Two slash-separated numbers that relate to queued signals for the real user ID of this process
- SigPnd – Number of pending signals for the thread and the process as a whole
- SigBlk – Signals being blocked
- SigIgn – Signals being ignored
- SigCgt – Signals being caught
Next, let’s dig a little deeper. Looking at just the signals caught, the first thing we need to do is convert the hexadecimal reported value to binary. You can do that with the xxd command:
$ echo 4b817efb | xxd -r -p | xxd -b 00000000: 01001011 10000001 01111110 11111011
This binary interpretation of the value allows you to pick out the individual signals, especially if you’re reasonably comfortable with binary numbers. Let’s break it down:
01001011 10000001 01111110 11111011 | | || | | |||||| ||||| || | | || | | |||||| ||||| |+-- 1 SIGHUP (hangup) | | || | | |||||| ||||| +--- 2 SIGINT (interrupt) | | || | | |||||| ||||+----- 4 SIGILL (illegal instruction) | | || | | |||||| |||+------ 5 SIGTRAP (trace/trap) | | || | | |||||| ||+------- 6 SIGABRT (abort) | | || | | |||||| |+-------- 7 SIGEMT (emulation trap) | | || | | |||||| +--------- 8 SIGFPE (floating point exception) | | || | | |||||+------------ 10 SIGBUS (bus error) | | || | | ||||+------------- 11 SIGSEGV (segmentation violation) | | || | | |||+-------------- 12 SIGSYS (bad system call) | | || | | ||+--------------- 13 SIGPIPE (broken pipe) | | || | | |+---------------- 14 SIGALRM (alarm) | | || | | +----------------- 15 SIGTERM (termination) | | || | +-------------------- 17 SIGUSR2 (user signal 2) | | || +--------------------------- 24 SIGTSTP (stop -- can be ignored) | | |+----------------------------- 25 SIGCONT (continue) | | +------------------------------ 26 SIGTTIN (terminal input) | +-------------------------------- 28 SIGVTALRM (timer expiration) +----------------------------------- 31 SIGXFSZ (file size exceeded)
Catching a signal requires that a signal handling function exists in the process to handle a given signal. The SIGKILL (9) and SIGSTOP (#) signals cannot be ignored or caught. For example, if you wanted to tell the kernel that ctrl-C's are to be ignored, you would include something like this in your source code:
signal(SIGINT, SIG_IGN);
To ensure that the default action for a signal is taken, you would do something like this instead:
signal(SIGSEGV, SIG_DFL);
The SigIgn (signal ignore) settings from the example above show that only four signals — 3 (SIGQUIT) and 20-22 (SIGWINCH, SIGURG, and SIGPOLL) are set to be ignored, while the SigBlk (signal block) settings block only the SIGPIPE
$ echo 380004 | xxd -r -p | xxd -b 00000000: 00111000 00000000 00000100 $ echo 10000 | xxd -r -p | xxd -b 00000000: 00010000 00000000
The same type of data for a watchdog process looks very different. Notice that it’s ignoring all signals.
# cat /proc/11/status | grep Sig SigQ: 0/15432 SigPnd: 0000000000000000 SigBlk: 0000000000000000 SigIgn: ffffffffffffffff SigCgt: 0000000000000000
Sending signals from command line
On the command line, there are as many ways to send signals to Unix processes as there are ways to skin a cat (actually more). Remember that “kill” doesn’t always mean termination, as the signals can do many things.
Killing by process ID (sends a SIGTERM)
kill 1234
Killing by process name *sends a SIGTERM)
pkill someproc
Killing more forcefully (sends a SIGKILL) — all do the same thing.
kill -9 1234 kill -KILL 1234 kill -SIGKILL 1234
Sending a specific signal — in this case SIGHUP
kill -HUP 1234 kill -SIGHUP 1234
You can send any signal in this way. Refer to the signal list below.
Listing signals
$ kill -L 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR1 11) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM 16) SIGSTKFLT 17) SIGCHLD 18) SIGCONT 19) SIGSTOP 20) SIGTSTP 21) SIGTTIN 22) SIGTTOU 23) SIGURG 24) SIGXCPU 25) SIGXFSZ 26) SIGVTALRM 27) SIGPROF 28) SIGWINCH 29) SIGIO 30) SIGPWR 31) SIGSYS 34) SIGRTMIN 35) SIGRTMIN+1 36) SIGRTMIN+2 37) SIGRTMIN+3 38) SIGRTMIN+4 39) SIGRTMIN+5 40) SIGRTMIN+6 41) SIGRTMIN+7 42) SIGRTMIN+8 43) SIGRTMIN+9 44) SIGRTMIN+10 45) SIGRTMIN+11 46) SIGRTMIN+12 47) SIGRTMIN+13 48) SIGRTMIN+14 49) SIGRTMIN+15 50) SIGRTMAX-14 51) SIGRTMAX-13 52) SIGRTMAX-12 53) SIGRTMAX-11 54) SIGRTMAX-10 55) SIGRTMAX-9 56) SIGRTMAX-8 57) SIGRTMAX-7 58) SIGRTMAX-6 59) SIGRTMAX-5 60) SIGRTMAX-4 61) SIGRTMAX-3 62) SIGRTMAX-2 63) SIGRTMAX-1 64) SIGRTMAX
Wrap-Up
On Unix systems, signals are used to send all kinds of information to running processes, and they come from user commands, other processes, and the kernel itself. Through /proc, information about how processes are handling systems is now easily accessible and, with just a little manipulation of the data, easy to understand.