|
perlipc - Perl interprocess communication (signals, fifos, pipes, safe subprocesses, sockets, and semaphores)
The basic IPC facilities of Perl are built out of the good old Unix
signals, named pipes, pipe opens, the Berkeley socket routines, and SysV
IPC calls. Each is used in slightly different situations.
Perl uses a simple signal handling model: the %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:
sub catch_zap {
my $signame = shift;
$shucks++;
die "Somebody sent me a SIG$signame";
}
$SIG{INT} = 'catch_zap';
$SIG{INT} = \&catch_zap;
Prior to Perl 5.7.3 it was necessary to do as little as you possibly
could in your handler; 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; particularly, memory allocation and I/O
routines are not. That meant that doing nearly anything in your
handler could in theory trigger a memory fault and subsequent core
dump - see Deferred Signals (Safe Signals) below.
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.
On most Unix platforms, the CHLD (sometimes also known as CLD) signal
has special behavior with respect to a value of 'IGNORE'.
Setting $SIG{CHLD} to 'IGNORE' on such a platform has the effect of
not creating zombie processes when the parent process fails to wait()
on its child processes (i.e. child processes are automatically reaped).
Calling wait() with $SIG{CHLD} set to 'IGNORE' usually returns
-1 on such platforms.
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 {
}
Sending a signal to a negative process ID means that you send the signal
to the entire Unix process-group. This code sends a hang-up signal to all
processes in the current process group (and sets $SIG{HUP} to IGNORE so
it doesn't kill itself):
{
local $SIG{HUP} = 'IGNORE';
kill HUP => -$$;
}
Another interesting signal to send is signal number zero. This doesn't
actually affect a child 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";
}
When directed at a process whose UID is not identical to that
of the sending process, signal number zero may fail because
you lack permission to send the signal, even though the process is alive.
You may be able to determine the cause of failure using %!.
unless (kill 0 => $pid or $!{EPERM}) {
warn "$pid looks dead";
}
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 reinstall themselves. Because Perl's signal mechanism is currently
based on the signal(3) function from the C library, you may sometimes be so
unfortunate 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;
$SIG{CHLD} = \&REAPER;
}
$SIG{CHLD} = \&REAPER;
or better still:
use POSIX ":sys_wait_h";
sub REAPER {
my $child;
while (($child = waitpid(-1,WNOHANG)) > 0) {
$Kid_Status{$child} = $?;
}
$SIG{CHLD} = \&REAPER;
}
$SIG{CHLD} = \&REAPER;
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);
alarm 0;
};
if ($@ and $@ !~ /alarm clock restart/) { die }
If the operation being timed out is system() or qx(), this technique
is liable to generate zombies. If this matters to you, you'll
need to do your own fork() and exec(), and kill the errant child process.
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.
A process that usually starts when the system boots and shuts down
when the system is shut down is called a daemon (Disk And Execution
MONitor). If a daemon process has a configuration file which is
modified after the process has been started, there should be a way to
tell that process to re-read its configuration file, without stopping
the process. Many daemons provide this mechanism using the SIGHUP
signal handler. When you want to tell the daemon to re-read the file
you simply send it the SIGHUP signal.
Not all platforms automatically reinstall their (native) signal
handlers after a signal delivery. This means that the handler works
only the first time the signal is sent. The solution to this problem
is to use POSIX signal handlers if available, their behaviour
is well-defined.
The following example implements a simple daemon, which restarts
itself every time the SIGHUP signal is received. The actual code is
located in the subroutine code(), which simply prints some debug
info to show that it works and should be replaced with the real code.
use POSIX ();
use FindBin ();
use File::Basename ();
use File::Spec::Functions;
$|=1;
my $script = File::Basename::basename($0);
my $SELF = catfile $FindBin::Bin, $script;
my $sigset = POSIX::SigSet->new();
my $action = POSIX::SigAction->new('sigHUP_handler',
$sigset,
&POSIX::SA_NODEFER);
POSIX::sigaction(&POSIX::SIGHUP, $action);
sub sigHUP_handler {
print "got SIGHUP\n";
exec($SELF, @ARGV) or die "Couldn't restart: $!\n";
}
code();
sub code {
print "PID: $$\n";
print "ARGV: @ARGV\n";
my $c = 0;
while (++$c) {
sleep 2;
print "$c\n";
}
}
A named pipe (often referred to as a FIFO) is an old Unix IPC
mechanism for processes communicating on the same machine. It works
just like a regular, connected anonymous pipes, except that the
processes rendezvous using a filename and don't have to be related.
To create a named pipe, use the POSIX::mkfifo() function.
use POSIX qw(mkfifo);
mkfifo($path, 0700) or die "mkfifo $path failed: $!";
You can also use the Unix command mknod(1) or on some
systems, mkfifo(1). These may not be in your normal path.
$ENV{PATH} .= ":/etc:/usr/etc";
if ( system('mknod', $path, 'p')
&& system('mkfifo', $path) )
{
die "mk{nod,fifo} $path failed";
}
A fifo is convenient when you want to connect a process to an unrelated
one. When you open a fifo, the program will block until there's something
on the other end.
For example, let's say you'd like to have your .signature file be a
named pipe that has a Perl program on the other end. Now every time any
program (like a mailer, news reader, finger program, etc.) tries to read
from that file, the reading program will block and your program will
supply the new signature. We'll use the pipe-checking file test -p
to find out whether anyone (or anything) has accidentally removed our fifo.
chdir;
$FIFO = '.signature';
while (1) {
unless (-p $FIFO) {
unlink $FIFO;
require POSIX;
POSIX::mkfifo($FIFO, 0700)
or die "can't mkfifo $FIFO: $!";
}
open (FIFO, "> |