Pipes con nombre

Que es

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.

Creando Pipes con Nombre

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 en Perl

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

Comunicando Procesos Mediante Pipes con Nombre

Ahora arrancamos la calculadora:

pp2@europa:~/src/perl/pipesconnombre$ bc < somepipe

En otra terminal tecleamos:

pp2@europa:~/src/perl/pipesconnombre$ cat > somepipe
2*3
4+9*5
CTRL-D
Vemos que en la terminal en la que tenemos la calculadora ejecutándose aparece:

pp2@europa:~/src/perl/pipesconnombre$ bc < somepipe
6
49

Acoplamiento de los Procesos que se Comunican

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.

Excepciones

Un proceso que intenta escribir a un pipe con nombre que carece de proceso lector recibe una señal SIGPIPE .

Comunicación Unidireccional entre Procesos Desacoplados usando Pipes con Nombre

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.

Comunicación Bidireccional entre Procesos Desacoplados usando Pipes con Nombre

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/bc2me
El 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:
        2
Ahora 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$

Ejercicios

  1. ¿Que hace la línea use POSIX qw{EWOULDBLOCK mkfifo}?
  2. ¿Que hace la línea use File::Temp qw/ tempfile tempdir /;
  3. ¿Que funciones provee IO::Prompt?
  4. ¿Para que sirve File::Spec?
  5. ¿Para que srive Time::HiRes?
  6. ¿Que contiene el hash %SIG?
  7. ¿En que módulo esta el método blocking?
  8. ¿Que devuelve sysread si no se leyó nada?
  9. ¿Que devuelve sysread si se leyó el final de fichero?
  10. Describa situaciones en las que podría ser ventajoso utilizar pipes con nombre



Subsecciones
Casiano Rodríguez León
2012-02-29