Background
Unix/Linux allows a user to have control over a program that they are running by sending what are called signals. These signals are then normally handled by the program in a way that is compliant with Unix/Linux standards. Two of the most important signals that are commonly sent to a program are called SIGTERM and SIGKILL. Those and a couple of others will be explored in this tutorial.
Basics
Send SIGTERM
The default signal sent by kill is signal 15 also known as SIGTERM. This is a friendly shutdown signal, which allows the process to get it’s things in order before exiting.
1 |
kill 1234 |
This can also be sent by explicitly telling kill what signal to send
1 |
kill -15 1235 |
Send SIGKILL
The next most common signal is 9 also known as SIGKILL. This is a stronger signal which does not allow the process to get it’s things in order before stopping it.
1 |
kill -9 1236 |
Send arbitrary signal
There are many other signals that can be registered and handled by a process. In my kernel they were found here: /usr/src/kernels/2.6.18-8.el5-i686/include/asm They can also be found here online. The following will send SIGUSR1
1 |
kill -10 1237 |
Experiment
Register SIGTERM
The following Python code will register a function called a signal handler to catch the SIGTERM signal.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env python import signal import sys def signal_handler_term(signal, frame): print "Caught SIGTERM signal" sys.exit(0) #signal.signal(signal.SIGTERM, signal_handler_term) while True: pass |
When we run this script, it will remain in memory until we issue the SIGTERM signal with the kill command
1 |
./signal-catcher.py |
On a different terminal, let’s find the correct process
1 |
ps -af | grep signal-catcher.py |
Notice that the process is still running
1 2 3 |
ps -ef | grep signal-catcher.py smccarty 8181 4753 86 14:48 pts/7 00:00:12 python ./signal-catcher.py smccarty 8183 4476 0 14:48 pts/6 00:00:00 grep signal-catcher.py |
Now issue the default signal for kill, SIGTERM
1 |
kill 8181 |
The process will display the message we set and exit
1 |
Caught SIGTERM signal |
Try it again with SIGKILL and notice the difference. Python was not allowed to handle the signal, instead it was killed by the OS (Linux)
1 |
kill -9 8267 |
The operating system displays a default message, but our process is never given a chance to catch the SIGKILL
1 |
Killed |
Register SIGKILL
Now let’s create a quick script to try and catch the SIGKILL signal.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
#!/usr/bin/env python import signal import sys def signal_handler_kill(signal, frame): print "Caught SIGKILL signal" sys.exit(0) signal.signal(signal.SIGKILL, signal_handler_kill) while True: pass |
Notice after trying to run this code, it will exit with an error
1 |
./signal-catcher.py |
Python will not allow us to register SIGKILL
1 2 3 4 |
Traceback (most recent call last): File "./signal-catcher.py", line 22, in <module> signal.signal(signal.SIGKILL, signal_handler_kill) RuntimeError: (22, 'Invalid argument') |
The standard C library does not allow a handler to be registered for the SIGKILL signal. The following C code from the Python source checks for SIG_ERR and propagates RuntimeError up the python stack. This code can be found under Modules/signalmodule.c if you are interested
1 2 3 4 |
if (PyOS_setsig(sig_num, func) == SIG_ERR) { PyErr_SetFromErrno(PyExc_RuntimeError); return NULL; } |
Also, from the documentation
1 2 3 4 |
* The :mod:`signal` module now performs tighter error-checking on the parameters to the :func:`signal.signal` function. For example, you can't set a handler on the :const:`SIGKILL` signal; previous versions of Python would quietly accept this, but 2.4 will raise a :exc:`RuntimeError` exception. |
Register SIGSTOP
As a fiinal experiment, try and register SIGSTOP. It has the same restriction that SIGKILL has. A process is not permitted to catch SIGSTOP. This is because SIGSTOP and SIGCONT are reserved for use by the operating system for job control.