~/

kill -9 and POSIX signals?!

It’s 3:30AM on a Saturday, and I’m currently working on a shell in Go. I’m trying to recall how to trap signals in Go, and kill -9 came to my mind. All I knew was that the command “terminates a program”, but why is it actually the case? I don’t understand it well, so I’m going to explore how the kill command and signal actually works.

Here’s what man kill shows:

SYNOPSIS
     kill [-s signal_name] pid ...
     kill -l [exit_status]
     kill -signal_name pid ...
     kill -signal_number pid ...

I didn’t know 9 is actually the signal number! :astonished: Interesting. So, a signal is actually used for sending information between processes or between the kernel and a process.

Here is a high level overview on how I think signals are used:

  • Doing kill -9 <pid> gets the kill utility to make a syscall to generate a SIGKILL signal in the kernel, and this terminates the process pid.
  • Whenever an invalid memory address is accessed, the kernel generates a SIGSEGV signal, and this results in the termination of the process with a segmentation fault.
  • Whenever the user presses Ctrl + C on the keyboard, the kernel sends the SIGINT signal to the shell process, which then sends it to the process.

There are many different signal types, and we can list all of them through kill -l or man signal.

bash-3.2$ kill -l
 1) SIGHUP       2) SIGINT       3) SIGQUIT      4) SIGILL
 5) SIGTRAP      6) SIGABRT      7) SIGEMT       8) SIGFPE
 9) SIGKILL     10) SIGBUS      11) SIGSEGV     12) SIGSYS
13) SIGPIPE     14) SIGALRM     15) SIGTERM     16) SIGURG
17) SIGSTOP     18) SIGTSTP     19) SIGCONT     20) SIGCHLD
21) SIGTTIN     22) SIGTTOU     23) SIGIO       24) SIGXCPU
25) SIGXFSZ     26) SIGVTALRM   27) SIGPROF     28) SIGWINCH
29) SIGINFO     30) SIGUSR1     31) SIGUSR2

The common ones are SIGHUP(1), SIGINT(2) (Ctrl + C), SIGQUIT(3) (Ctrl + D), SIGKILL(9) and SIGTERM(15). By default, kill without the signal name will use SIGTERM. This will ask the process to terminate as soon as it possibly can (i.e. graceful shutdown).

So, how can we actually get the shell process to send it to the inner process? We need traps. Traps allow us to trap some or all of these signals that the kernel sends to the process, and the process could then perform operations on the trapped signal.

We can create a trap for specific signals in our shell process, and when it receives those signals, it can act accordingly, such as forwarding it to the inner process or terminating it, in the case of a SIGINT.

Also, a quick look at the linux kernel source code shows the following fact:

SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.

Takeaways

  • kill -9 <pid> can be translated into:
kill -9 <pid>
kill -SIGKILL <pid>
kill -KILL <pid>
kill SIGKILL <pid>
  • Don’t run kill -9 just because kill doesn’t work. Only do this as a last resort.
  • You cannot SIGKILL pid 1 (init on Linux / launchd on Mac OSX). This is done to assure that the system is not brought down accidentally.

I should probably play with strace someday. I wonder what shows up for these kill syscalls.

Resources