Perl supports many facilities for Interprocess Communication (IPC), including signals and sockets. Regardless of which IPC form you use with Perl, you’ll almost always tangle with signals.
Signals can be generated from any source, including key sequences on your keyboard, an event on your system, or from a failed program.
For quite some time, Perl has used a simple signal handling
module, in which %SIG
was populated with keys that represented the signal
handlers your system supported. You’d invoke one of these handlers
when Perl came across $SIG{'whatever_signal_here'}
. In fact, even
in Perl 5.8, the %SIG
interface is
the same, but the behavior of signals is entirely different. More on
this shortly.
For signals to be useful, they must be trapped and dealt with.
However, you shouldn’t try to do too much when you trap a signal.
Generally, you should only generate a warning and deal with the
condition that caused the error. In a way, Perl’s %SIG
keeps you from doing too much. %SIG
works like this:
$SIG{I_GOT_THIS_SIGNAL} = \&now_call_this_sub_to_handle_it; sub now_call_this_sub_to_handle it { ... }
If you need to know which signals Perl is aware of, do a
'kill -l'
on a Unix system, or use
Config.pm:
#!/usr/local/bin/perl -w use Config; print "Perl knows about the following signals:\n"; print $Config{sig_name_init}, "\n";
Here’s a simple snippet that lets you know if someone sent you a
HUP
signal:
$SIG{HUP} = \&hic_hup; sub hic_hup { my $signal = shift; # Don't die for now. Just warn us. warn "Somebody sent me a SIG${signal}\n"; }
Often, the type of signal you encounter in your Perl program
will dictate how you should handle it. For example, you should assign
'IGNORE'
to the 'CHLD'
signal on many Unix platforms to
reduce the possibility of creating zombie processes. Other signals,
such as KILL
and STOP
, cannot and should not be ignored so
easily. For this purpose, Perl lets you use a local( )
statement, which lets you temporarily ignore a
signal:
sub kill_handler { local $SIG{KILL} = 'IGNORE'; # Inherited by all functions here i_will_settle_for_denial(); } sub i_will_settle_for_denial { # KILL ignored for now. Go in peace. }
Unix allows you to kill processes with negative process IDs by
sending a signal zero. If you have a program
that starts itself and several children, use 'kill -PARENT_PID'
to kill the parent and
all of the children processes. But obviously, if you send a HUP
to the parent, you don’t want the parent
to die. You can avoid this with Perl and signals:
sub be_nice_to_your_parents { local($SIG{HUP}) = 'IGNORE'; kill('HUP', -$$); # This pid and its kids }
Naturally, you don’t have to do anything fancy with signal
handlers. You can simply die if the given $SIG{NAME}
won’t cause negative effects on
your system if you mishandle it:
$SIG{INT} = sub { die "\nYou've interrupted me for one last time!\n" };
Keep in mind that it’s not all fun and games with Perl and signals. If you don’t know how your system’s C library and its signals implementation behave, or if you haven’t read the instructions before firing your BB gun, you’ll shoot your eye out. We guarantee it!
As of Perl 5.8, you can probably be a little more confident in Perl’s ability to handle even a bad system-specific signals implementation. In the old days, when men were men and eyeless men were everywhere, a bad signals implementation at both a system and Perl level, or a signal cropping up at the wrong time, could corrupt Perl’s internal state. Thankfully, Perl 5.8 and later will postpone signal handling until it’s safe to proceed. This means that while the signals interface doesn’t change, even if your program catches a signal at a specific place, Perl 5.8 and later will finish whatever they are doing when the signal is encountered.
Finally, signals aren’t limited to coping with the behaviors of
processes. You can also trap alarm signals for Perl functions and
system calls alike if your Perl code lives in an eval( )
block. For example, let’s say you
want to ping a host, your-host.your.domain
, and you want the ping
process to timeout in a reasonable amount of time if your-host.your.domain
isn’t available. You
can use alarm( )
inside of eval( )
, like so:
#!/usr/local/bin/perl -w my $to_ping = 'your-host.your.domain'; my $a_secs = 5; eval { local $SIG{ALRM} = sub { die "no dice for $to_ping" }; alarm $a_secs; system("/usr/sbin/ping", $to_ping); alarm 0; }; if ($@ and $@ !~ /no dice for $to_ping/) { die }
Note that you may have to wait for the ping command itself to
timeout. While alarm
occurs after
$a_secs
, you won’t see the “no dice
for . . . " message until system( )
(or qx( )
) is complete. This is not
the case for Perl functions such as flock(
)
, open( )
,
etc.
Get Perl in a Nutshell, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.