One method of interprocess communication is to send and receive signals. A signal is a one-bit message (meaning "this signal happened") sent to a process from another process or from the kernel. Signals are numbered, usually from one to some small number like 15 or 31. Some signals have predefined meanings and are sent automatically to a process under certain conditions (such as memory faults or floating-point exceptions); others are strictly user-generated from other processes. Those processes must have permission to send such a signal. Only if you are the superuser or if the sending process has the same user ID as the receiving process is the signal permitted.
The response to a signal is called the signal's action. Predefined signals have certain useful default actions, such as aborting the process or suspending it. Other signals are completely ignored by default. Nearly all signals can have their default action overridden, to either be ignored or else caught (invoking a user-specified section of code automatically).
So far, this is all standard stuff; here's where it gets Perl-specific. When a Perl process catches a signal, a subroutine of your choosing gets invoked asynchronously and automatically, momentarily interrupting whatever was executing. When the subroutine exits, whatever was executing resumes as if nothing had happened (except for the actions performed by the subroutine, if any).
Typically, the signal-catching subroutine will do one of two things: abort the program after executing some cleanup code, or set some flag (such as a global variable) that the program routinely checks.[6]
[6] In fact, doing anything more complicated than this is likely to mess things up; most of Perl's inner workings do not like to be executed in the main program and from the subroutine at the same time. Neither do your system libraries.
You need to know the signal names to register a signal handler with Perl. By registering a signal handler, Perl will call the selected subroutine when the signal is received.
Signal names are defined in the signal (2) manpage, and usually also in the C include file /usr/include/sys/signal.h. Names generally start with SIG
, such as SIGINT
, SIGQUIT
, and SIGKILL
. To declare the subroutine my_sigint_catcher()
as the signal handler to deal with the SIGINT
, we set a value into the magic %SIG
hash. In this hash, we set the value of the key INT
(that's SIGINT
without the SIG
) to the name of the subroutine that will catch the SIGINT
signal, like so:
$SIG{'INT'} = 'my_sigint_catcher';
But we also need a definition for that subroutine. Here's a simple one:
sub my_sigint_catcher { $saw_sigint = 1; # set a flag }
This signal catcher sets a global variable and then returns immediately. Returning from this subroutine causes execution to resume wherever it was interrupted. Typically, you'd first zero the $saw_sigint
flag, set this subroutine up as a SIGINT
catcher, and then do your long-running routine, like so:
$saw_sigint = 0; # clear the flag $SIG{'INT'} = 'my_sigint_catcher'; # register the catcher foreach (@huge_array) { # do something # do more things # do still more things if ($saw_sigint) { # interrupt wanted? # some sort of cleanup here last; } } $SIG{'INT'} = 'DEFAULT'; # restore the default action
The trick here is that the value of the flag is checked at useful points during the evaluation and is used to exit the loop prematurely, here also handling some cleanup actions. Note the last statement in the preceding code: setting the action to DEFAULT
restores the default action on a particular signal (another SIGINT
will abort the program immediately). Another useful special value like this is IGNORE
, meaning to ignore the signal (if the default action is not to ignore the signal, like SIGINT
). You can make a signal action IGNORE
if no cleanup actions are required, and you don't want to terminate operations early.
One of the ways that the SIGINT
signal is generated is by having the user press the selected interrupt character (like CTRL-C) on the terminal. But a process can also generate the SIGINT
signal directly using the kill
function. This function takes a signal number or name, and sends that signal to the list of processes (identified by process ID) following the signal. So sending a signal from a program requires determining the process IDs of the recipient processes. (Process IDs are returned from some of the functions, such as fork
and - when opening a program as a filehandle - open
). Suppose you want to send a signal 2 (also known as SIGINT
) to the processes 234 and 237. It's as simple as this:
kill(2,234,237); # send SIGINT to 234 and 237 kill ('INT', 234, 237); #same
For more about signal handling, see Chapter 6 of Programming Perl or the perlipc (1) manpage.