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.