Un pipe con nombre también llamada FIFO, es un viejo mecanismo Unix para la comunicación entre procesos en la misma máquina. En MacOS se denominan sockets, pero no hay que confundirlos con los sockets TCP. Este concepto también se encuentra en Windows, si bien implementado con otra semántica. Funciona como un pipe normal.
Un pipe tradicional es anónimo en el sentido de que sólo existe durante la existencia de los procesos implicados y no tiene un nombre en el sistema operativo. Un pipe con nombre es persistente, sobreviviendo a los procesos que lo usan: la finalización del proceso no implica la desaparición del pipe. Además dispone de un nombre/dirección en la jerarquía de archivos.
Su utilidad se revela a la hora de conectar procesos no relacionados.
Para hacer un pipe con nombre, en Unix usamos mkfifo:
pp2@europa:~/src/perl/pipesconnombre$ mkfifo somepipe pp2@europa:~/src/perl/pipesconnombre$ ls -l somepipe prw-r--r-- 1 pp2 pp2 0 2009-03-11 14:14 somepipe
Obsérvese la p
como primer carácter, indicando que no se trata de un
archivo ordinario sino de un pipe con nombre.
También podríamos haberlo creado con mknod:
mknod somepipe p
La función mkfifo esta disponible en el módulo POSIX:
europa:~/src/perl/pipesconnombre$ perl -MPOSIX=mkfifo -wde 0 main::(-e:1): 0 DB<1> mkfifo "somepipe", 0755 DB<2> !!ls -l somepipe prwxr-xr-x 1 pp2 pp2 0 2009-03-11 14:29 somepipe
pp2@europa:~/src/perl/pipesconnombre$ bc < somepipe
En otra terminal tecleamos:
pp2@europa:~/src/perl/pipesconnombre$ cat > somepipe 2*3 4+9*5 CTRL-DVemos que en la terminal en la que tenemos la calculadora ejecutándose aparece:
pp2@europa:~/src/perl/pipesconnombre$ bc < somepipe 6 49
Obsérvese que en los pipes anónimos los procesos que comparten el pipe comparten un ancestro cercano en la jerarquía de procesos. Esto no es tan cierto en el caso de los pipes con nombre.
Un proceso que intenta escribir a un pipe con nombre que carece de proceso lector recibe una señal SIGPIPE .
Cuando uso mozilla y quiero imprimir una página solo se ofrecen dos opciones: mandarlo a impresora (lo que sólo hago cuando estoy seguro que el texto me interesa mucho) o guardarlo como un postscript (que ocupa muchos megas). Una solución es ejecutar antes de hacerlo el siguiente programa:
nereida:~# cat -n /usr/local/bin/mozilla2pdf 1 #!/usr/bin/perl -w 2 use strict; 3 4 my $visualpdf = "xpdf"; # gpdf acroread 5 my $pipe = (shift || '/tmp/mozilla.ps'); 6 my $pdf = (shift || '/tmp/mozilla.pdf'); 7 unless (-p $pipe) { 8 unlink $pipe; 9 system("mkfifo $pipe"); 10 die "Can't execute mkfifo" if $?; 11 } 12 13 while (1) { 14 system("ps2pdf - $pdf < $pipe"); 15 die "Pipe with ps2pdf did'nt work" if $?; 16 system("$visualpdf $pdf"); 17 die "Error executing " if $?; 18 } 19 20 =head1 NAME mozilla2pdf 21 22 =head1 SYNOPSIS 23 24 mozilla2pdf ps_pipe pdf_file 25 26 =head1 DESCRIPTION 27 28 Print from your browser the HTML page to postscript file C<ps_pipe>. 29 It will be saved as a C<pdf_file>. 30 If C<ps_pipe> isn't specified defaults to C</tmp/mozilla.ps>. 31 If C<pdf_file> isn't specified defaults to C</tmp/mozilla.pdf>. 32 33 =head1 AUTHOR 34 35 Casiano Rodriguez Leon
Después de ejecutar este programa seleccionamos la opción
imprimir a fichero de Mozilla. Le indicamos que lo haga
en el fichero /tmp/mozilla.ps
. Nos avisa que ya existe,
le decimos que adelante y el pipe comienza a trabajar.
Al finalizar la conversión de la línea 8 se abre un
visualizador (hemos usado xpdf
) para mostrar
el aspecto del .pdf
.
Parece que no es posible utilizar con garantías pipes con nombre en un sistema de archivos NFS.
El siguiente código muestra como usar pipes con nombre para comunicar con una aplicación externa: la calculadora bc.
pp2@europa:~/src/perl/pipesconnombre$ cat -n bidirwithnamedpipes.pl 1 #!/usr/bin/perl -w 2 use strict; 3 use POSIX qw{EWOULDBLOCK mkfifo}; 4 use File::Temp qw/ tempfile tempdir /; 5 use IO::Prompt; 6 use File::Spec; 7 use Time::HiRes qw{usleep}; 8 9 my $tmp = File::Spec->tmpdir(); 10 my $dir = tempdir( "$tmp/PIPEEXAMPLEXXXX", CLEANUP => 1 ); 11 my $out = "$dir/me2bc"; 12 my $in = "$dir/bc2me"; 13 14 mkfifo $out, 0755; 15 mkfifo $in, 0755; 16 17 $SIG{PIPE} = sub { 18 warn "received SIGPIPE. Exiting!\n"; 19 exit(1); 20 }; 21 22 prompt "Run \n\tbc < $out &> $in\n". 23 "in another terminal, then press enter: "; 24 25 open (my $fout, "> $out"); 26 open (my $fin, "$in"); 27 $fin->blocking(0); 28 29 my $result; 30 while (prompt("Expression (CTRL-D to end): ", '-l')) { 31 32 print "Sending expression to external 'bc' process ...\n"; 33 syswrite $fout, $_; 34 usleep(10); 35 my $bytes = sysread $fin, $result, 1024; 36 if (defined($bytes)) { 37 if ($bytes) { 38 print "Obtained result from external 'bc' process:\n\t$result"; 39 } 40 else { # EOF 41 warn "bc process broken!\n"; 42 last; 43 } 44 } 45 else { 46 print "Nothing has arrived (yet) ...\n"; 47 } 48 }
La función sysread
cuando se usa sobre un manejador sin bloqueo
devuelve undef
si no hay nada disponible.
En tal caso la variable $!
contiene el
código de error EWOULDBLOCK
(definida en POSIX).
Esta situación no debe confundirse con la devolución de un cero,
la cual indica la presencia del final de fichero.
El manejo de escrituras sin bloqueo es similar.
El retorno de un valor undef
señala la imposibilidad
de escribir. Si la escritura sólo se ha podido hacer parcialmente
es responsabilidad del programador intentarlo posteriormente.
Ahora ejecutamos el programa en una terminal:
pp2@europa:~/src/perl/pipesconnombre$ ./bidirwithnamedpipes.pl Run bc < /tmp/PIPEEXAMPLEgVQX/me2bc &> /tmp/PIPEEXAMPLEgVQX/bc2me in another terminal, then press enter:
Sigamos las instrucciones: copiamos la línea en otra terminal:
pp2@europa:~$ bc < /tmp/PIPEEXAMPLEgVQX/me2bc &> /tmp/PIPEEXAMPLEgVQX/bc2meEl proceso se queda en espera. Obśervese que hemos redirigido tanto la salida estandar (
stdout
) como la salida de errores (stderr
)
al pipe con nombre /tmp/PIPEEXAMPLEgVQX/bc2me
(Véase por ejemplo
BASH Programming - Introduction HOW-TO).
Ahora continuamos en la otra terminal introduciendo expresiones:
Expression (CTRL-D to end): 2*3 Sending expression to external 'bc' process ... Obtained result from external 'bc' process: 6 Expression (CTRL-D to end): 4+5 Sending expression to external 'bc' process ... Obtained result from external 'bc' process: 9 Expression (CTRL-D to end): a=2 Sending expression to external 'bc' process ... Nothing has arrived (yet) ... Expression (CTRL-D to end): a Sending expression to external 'bc' process ... Obtained result from external 'bc' process: 2Ahora pulsamos
CTRL-C
en la terminal en la que
esta ejecutándose bc
.
El proceso correspondiente a ./bidirwithnamedpipes.pl
detecta la señal de SIGPIPE
una vez leída
la entrada:
Expression (CTRL-D to end): Sending expression to external 'bc' process ... received SIGPIPE. Exiting! pp2@europa:~/src/perl/pipesconnombre$
use
POSIX
qw{EWOULDBLOCK mkfifo}
?
use
File::Temp
qw/ tempfile tempdir /
;
%SIG
?
blocking
?
sysread
si no se leyó nada?
sysread
si se leyó el final de fichero?