ASPN ActiveState Programmer Network
  ActiveState
/ Home / Perl / PHP / Python / Tcl / XSLT /
/ Safari / My ASPN /
Cookbooks | Documentation | Mailing Lists | Modules | News Feeds | Products | User Groups | Web Services
SEARCH
advanced | search help

Reference
ActivePerl 5.10
Core Documentation
perl
perlintro
perltoc
perlreftut
perldsc
perllol
perlrequick
perlretut
perlboot
perltoot
perltooc
perlbot
perlstyle
perlcheat
perltrap
perldebtut
perlfaq
perlfaq1
perlfaq2
perlfaq3
perlfaq4
perlfaq5
perlfaq6
perlfaq7
perlfaq8
perlfaq9
perlsyn
perldata
perlop
perlsub
perlfunc
perlopentut
perlpacktut
perlpod
perlpodspec
perlrun
perldiag
perllexwarn
perldebug
perlvar
perlre
perlrebackslash
perlrecharclass
perlreref
perlref
perlform
perlobj
perltie
perldbmfilter
perlipc
perlfork
perlnumber
perlthrtut
perlothrtut
perlport
perllocale
perluniintro
perlunicode
perlunifaq
perlunitut
perlebcdic
perlsec
perlmod
perlmodlib
perlmodstyle
perlmodinstall
perlnewmod
perlpragma
perlutil
perlcompile
perlfilter
perlglossary
perlembed
perldebguts
perlxstut
perlxs
perlclib
perlguts
perlcall
perlreapi
perlreguts
perlapi
perlintern
perliol
perlapio
perlhack
perlbook
perlcommunity
perltodo
perldoc
perlhist
perldelta
perl5100delta
perl595delta
perl594delta
perl593delta
perl592delta
perl591delta
perl590delta
perl588delta
perl587delta
perl586delta
perl585delta
perl584delta
perl583delta
perl582delta
perl581delta
perl58delta
perl573delta
perl572delta
perl571delta
perl570delta
perl561delta
perl56delta
perl5005delta
perl5004delta
perlartistic
perlgpl
perlcn
perljp
perlko
perltw
perlaix
perlamiga
perlapollo
perlbeos
perlbs2000
perlce
perlcygwin
perldgux
perldos
perlepoc
perlfreebsd
perlhpux
perlhurd
perlirix
perllinux
perlmachten
perlmacos
perlmacosx
perlmint
perlmpeix
perlnetware
perlopenbsd
perlos2
perlos390
perlos400
perlplan9
perlqnx
perlriscos
perlsolaris
perlsymbian
perltru64
perluts
perlvmesa
perlvms
perlvos
perlwin32

MyASPN >> Reference >> ActivePerl 5.10 >> Core Documentation
ActivePerl 5.10 documentation

NAME

perlipc - Perl interprocess communication (signals, fifos, pipes, safe subprocesses, sockets, and semaphores)


DESCRIPTION

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.


Signals

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';  # could fail in modules
    $SIG{INT} = \&catch_zap;  # best strategy

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 {
        # 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 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 => -$$;
        # snazzy writing of: 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;
        # 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 better still:

    use POSIX ":sys_wait_h";
    sub REAPER {
        my $child;
        # If a second child dies while in the signal handler caused by the
        # first death, we won't get another signal. So must loop here else
        # we will leave the unreaped child as a zombie. And the next time
        # two children die we get another zombie. And so on.
        while (($child = waitpid(-1,WNOHANG)) > 0) {
            $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 }

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.

Handling the SIGHUP Signal in Daemons

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.

  #!/usr/bin/perl -w
  use POSIX ();
  use FindBin ();
  use File::Basename ();
  use File::Spec::Functions;
  $|=1;
  # make the daemon cross-platform, so exec always calls the script
  # itself with the right path, no matter how the script was invoked.
  my $script = File::Basename::basename($0);
  my $SELF = catfile $FindBin::Bin, $script;
  # POSIX unmasks the sigprocmask properly
  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";
      }
  }
  __END__
  


Named Pipes

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.

    # system return val is backwards, so && not ||
    #
    $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; # go home
    $FIFO = '.signature';
    while (1) {
        unless (-p $FIFO) {
            unlink $FIFO;
            require POSIX;
            POSIX::mkfifo($FIFO, 0700)
                or die "can't mkfifo $FIFO: $!";
        }
        # next line blocks until there's a reader
        open (FIFO, ">