%SIG
hash contains names or references of user-installed signal handlers. These handlers will be called with an argument which is the name of the signal that triggered it.
A signal may be generated intentionally from a particular keyboard sequence like control-C or control-Z, sent to you from another process, or triggered automatically by the kernel when special events transpire, like a child process exiting, your process running out of stack space, or hitting file size limit.
For example, to trap an interrupt signal, set up a handler like this.
Notice how all we do is set a global variable and then raise an exception.
That's because on most systems libraries are not re-entrant, so calling any
print
functions (or even anything that needs to
malloc
more memory) could in theory trigger a memory fault and
subsequent core dump.
sub catch_zap { my $signame = shift; $shucks++; die "Somebody sent me a SIG$signame"; } $SIG{INT} = 'catch_zap'; # could fail in modules $SIG{INT} = \&catch_zap; # best strategy
The names of the signals are the ones listed out by kill -l
on your system, or you can retrieve them from the Config module. Set up an
@signame
list indexed by number to get the name and a
%signo
table indexed by name to get the number:
use Config; defined $Config{sig_name} || die "No sigs?"; foreach $name (split(' ', $Config{sig_name})) { $signo{$name} = $i; $signame[$i] = $name; $i++; }
So to check whether signal 17 and SIGALRM were the same, do just this:
print "signal #17 = $signame[17]\n"; if ($signo{ALRM}) { print "SIGALRM is $signo{ALRM}\n"; }
You may also choose to assign the strings 'IGNORE'
or 'DEFAULT'
as the handler, in which case Perl will try to discard the signal or do the default thing. Some signals can be neither trapped nor ignored, such as the
KILL and
STOP (but not the
TSTP) signals. One strategy for temporarily ignoring signals is to use a local
statement, which will be automatically restored once your block is exited. (Remember that local
values are ``inherited'' by functions called from within that block.)
sub precious { local $SIG{INT} = 'IGNORE'; &more_functions; } sub more_functions { # interrupts still ignored, for now... }
Sending a signal to a negative process ID means that you send the signal to the entire Unix process-group. This code send a hang-up signal to all processes in the current process group except for the current process itself:
{ local $SIG{HUP} = 'IGNORE'; kill HUP => -$$; # snazzy writing of: kill('HUP', -$$) }
Another interesting signal to send is signal number zero. This doesn't actually affect another process, but instead checks whether it's alive or has changed its UID.
unless (kill 0 => $kid_pid) { warn "something wicked happened to $kid_pid"; }
You might also want to employ anonymous functions for simple signal handlers:
$SIG{INT} = sub { die "\nOutta here!\n" };
But that will be problematic for the more complicated handlers that need to re-install themselves. Because Perl's signal mechanism is currently based on the signal
function from the
C library, you may sometimes be so misfortunate as to run on systems where that function is ``broken'', that is, it behaves in the old unreliable SysV way rather than the newer, more reasonable
BSD and
POSIX fashion. So you'll see defensive people writing signal handlers like this:
sub REAPER { $waitedpid = wait; # loathe sysV: it makes us not only reinstate # the handler, but place it after the wait $SIG{CHLD} = \&REAPER; } $SIG{CHLD} = \&REAPER; # now do something that forks...
or even the more elaborate:
use POSIX ":sys_wait_h"; sub REAPER { my $child; while ($child = waitpid(-1,WNOHANG)) { $Kid_Status{$child} = $?; } $SIG{CHLD} = \&REAPER; # still loathe sysV } $SIG{CHLD} = \&REAPER; # do something that forks...
Signal handling is also used for timeouts in Unix, While safely protected
within an eval{} block, you set a signal handler to trap alarm signals and then schedule to
have one delivered to you in some number of seconds. Then try your blocking
operation, clearing the alarm when it's done but not before you've exited
your eval{} block. If it goes off, you'll use die
to jump out of the
block, much as you might using longjmp
or throw
in other languages.
Here's an example:
eval { local $SIG{ALRM} = sub { die "alarm clock restart" }; alarm 10; flock(FH, 2); # blocking write lock alarm 0; }; if ($@ and $@ !~ /alarm clock restart/) { die }
For more complex signal handling, you might see the standard POSIX module. Lamentably, this is almost entirely undocumented, but the t/lib/posix.t file from the Perl source distribution has some examples in it.