In this clip, we'll be looking at signals, what they are, and some suggestions of what to do with them.
A signal is an event that's delivered to a process by the kernel.
Most of them originate from outside the program and are not directly related to what the program is doing,
which is why I refer to them as asynchronous. They can occur at any time.
A signal says to a process, Something potentially interesting has happened, which you may want to respond to.
A process chooses to respond to a signal by installing a signal handler, which in our case means a Python function.
There are quite a number of signal types, and a few are shown in this table.
There's a more detailed discussion of signals in my Pluralsight course on Linux Systems Programming.
You notice that each signal has a name and a corresponding number. SIGHUP, signal #1, is interpreted by some
services to mean, We've changed your configuration file, and you need to go and re-read it.
HUP stands for hang-up. It harks back to an earlier use of the signal, which was to do with modems and
dial-up lines. SIGINT is a signal that is sent by terminal driver when you type Ctrl+C.
SIGTERM is intended as a polite please clean up and terminate signal.
If you use the kill command on the command line and don't specify signal type, this is the one that you'll get.
SIGUSR1 is a sort of general purpose user-defined signal. And SIGALRM gets sent on expiry of an alarm clock timer.
I'll show you an example of that later.
Normally a signal is delivered directly to a process, and its handler (if there is one) is part of the code
of that process. But because Python is an interpreted language, when you run a program, the code that's
really running is the Python interpreter and virtual machine. And that's where the signals get delivered.
The runtime's low-level signal handler sets a flag which tells the virtual machine to execute the
corresponding Python signal handler at a later point. So in a Python program, you're getting the signals
secondhand so to speak. This means that it makes no sense to try and catch like SIGSEGV, which relate to
behavior of the underlying machine code.
Just coming back to the table for a moment, the column headed Default Action shows what will happen if a
signal arrives and you haven't installed a handler for it. Term means that the program will be terminated.
Core means that it will be terminated, but it may also write a core file, a memory image of the program at the time.
A few signals, though none of the ones that are in this table, are ignored by default.
Now the program I'm going to use as a basis for demonstration is a classic and entirely useless program.
It builds a list of prime numbers. Here's the function which tests a number for primeness.
It's not the most efficient way of doing this, but that's not important here.
In fact as you'll notice, I've put a 100-msec sleep in here to deliberate slow things down anyway.
And here's the main loop, an infinite loop over all integer values, testing for primeness, and adding them
to a list. Now let's run this. And here we go happily counting primes.
If I type Ctrl+C, that keypress is in fact trapped by the terminal driver, which delivers a SIGINT to the process.
And in the Python case, the SIGINT is handled by the Python runtime, which responds by throwing a
KeyboardInterrupt exception. The end result, of course, is that the program is terminated.
We can tell the program to ignore these SIGINT signals with these two extra lines of code.
The import star says to import all of the symbols from the signal module.
This usage is generally frowned upon because of the risk of creating name clashes.
I'm using it here as an expedient because, otherwise, I end up with signal. scattered all over the code.
And this is the call that's establishing the handler for SIGINT signals, or rather, in this case,
it's using the special value SIG_IGN to say that we want to ignore this signal type.
Without the import star, we would need signal. in front of all of those identifiers in that line.
So let's run it again. Now if I type Ctrl+C, you'll see that it's echoed.
I'll do this again. It's echoed, but it has no effect. But there's one other special character that's
trapped by the terminal driver, Ctrl+\, which results in delivery of a SIGQUIT signal.
Since we're not catching that, if I type Ctrl+\, then the program is terminated potentially with a core dump.
Here we've added two more lines to ignore both SIGQUIT AND SIGTERM.
And if we run that, then we'll see that Ctrl+C is ignored. It is echoed.
And we'll also see that Ctrl+\ is echoed but ignored. So are we doomed to calculate prime numbers forever now?
Well, no. We can send other signals using a shell in another terminal like this.
That actually has no effect because, by default, we're sending a SIGTERM, and we're ignoring SIGTERM.
But, of course, there's one signal that we cannot catch or ignore, and that's the kill signal.
That's a sure kill and, indeed, you see in the left-hand window that the program has been terminated.