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 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';
$ENV{PATH} .= ":/etc:/usr/games";
while (1) {
unless (-p $FIFO) {
unlink $FIFO;
system('mknod', $FIFO, 'p')
&& die "can't mknod $FIFO: $!";
}
# next line blocks until there's a reader
open (FIFO, "> $FIFO") || die "can't write $FIFO: $!";
print FIFO "John Smith (smith\@host.org)\n", `fortune -s`;
close FIFO;
sleep 2; # to avoid dup signals
}
|
|
In Perls before Perl 5.7.3 by installing Perl code to deal with signals, you were exposing
yourself to danger from two things. First, few system library functions are re-entrant. If the
signal interrupts while Perl is executing one function (like malloc(3) or printf(3)), and your
signal handler then calls the same function again, you could get unpredictable behavior--often,
a core dump. Second, Perl isn't itself re-entrant at the lowest levels. If the signal interrupts
Perl while Perl is changing its own internal data structures, similarly unpredictable behaviour
may result.
There were two things you could do, knowing this: be paranoid or be pragmatic. The paranoid
approach was to do as little as possible in your signal handler. Set an existing integer
variable that already has a value, and return. This doesn't help you if you're in a slow system
call, which will just restart. That means you have to die to longjump(3) out of the
handler. Even this is a little cavalier for the true paranoiac, who avoids die in a
handler because the system is out to get you. The pragmatic approach was to say ``I know
the risks, but prefer the convenience'', and to do anything you wanted in your signal handler,
and be prepared to clean up core dumps now and again.
In Perl 5.7.3 and later to avoid these problems signals are "deferred"-- that is
when the signal is delivered to the process by the system (to the C code that implements Perl) a
flag is set, and the handler returns immediately. Then at strategic "safe" points in
the Perl interpreter (e.g. when it is about to execute a new opcode) the flags are checked and
the Perl level handler from %SIG is executed. The "deferred" scheme allows much more
flexibility in the coding of signal handler as we know Perl interpreter is in a safe state, and
that we are not in a system library function when the handler is called. However the
implementation does differ from previous Perls in the following ways:
- Long running opcodes
- As Perl interpreter only looks at the signal flags when it about to execute a new opcode
if a signal arrives during a long running opcode (e.g. a regular expression operation on a
very large string) then signal will not be seen until operation completes.
- Interrupting IO
-
When a signal is delivered (e.g. INT control-C) the operating system breaks into IO
operations like read (used to implement Perls <> operator). On older
Perls the handler was called immediately (and as read is not "unsafe"
this worked well). With the "deferred" scheme the handler is not called
immediately, and if Perl is using system's stdio library that library may
re-start the read without returning to Perl and giving it a chance to call the
%SIG handler. If this happens on your system the solution is to use :perlio
layer to do IO - at least on those handles which you want to be able to break into with
signals. (The :perlio layer checks the signal flags and calls %SIG handlers
before resuming IO operation.)
Note that the default in Perl 5.7.3 and later is to automatically use the :perlio
layer.
- Signals as "faults"
- Certain signals e.g. SEGV, ILL, BUS are generated as a result of virtual memory or other
"faults". These are normally fatal and there is little a Perl-level handler can do
with them. (In particular the old signal scheme was particularly unsafe in such cases.)
However if a %SIG handler is set the new scheme simply sets a flag and returns as described
above. This may cause the operating system to try the offending machine instruction again
and - as nothing has changed - it will generate the signal again. The result of this is a
rather odd "loop". In future Perl's signal mechanism may be changed to avoid this
- perhaps by simply disallowing %SIG handlers on signals of that type. Until then the
work-round is not to set a %SIG handler on those signals. (Which signals they are is
operating system dependant.)
- Signals triggered by operating
system state
- On some operating systems certain signal handlers are supposed to "do something"
before returning. One example can be CHLD or CLD which indicates a child process has
completed. On some operating systems the signal handler is expected to
wait for
the completed child process. On such systems the deferred signal scheme will not work for
those signals (it does not do the wait). Again the failure will look like a
loop as the operating system will re-issue the signal as there are un-waited-for completed
child processes.
|
|