|
El primer usuario inicia el diálogo con el comando
$ mytalk.pl user
. El otro usuario contesta con un comando
np5.pl -PIDNUMBER /tmp/tmpdir
.
Este ejemplo muestra como usar dos FIFOs o pipes con nombre para establecer una comunicación bidireccional entre dos procesos. Los dos procesos usan los dos pipes en direcciones opuestas.
lhp@nereida:~/Lperl/src/expect/kibitz$ cat -n mytalk.pl 1 #!/usr/bin/perl -w 2 use strict; 3 use IO::Handle; 4 use File::Basename; 5 use File::Temp qw/tempdir/; 6 use IO::Select; 7 8 my ($pgm)=basename($0); 9 my $inf; 10 my $outf; 11 my @handler = (\&user1, \&user2); 12 13 &usage unless @ARGV; 14 # user 1 invokes mytalk.pl as "mytalk.pl user" 15 # user 2 writes "mytalk.pl /tmp/cSmOql4G3i/mytalk.pl -16080 /tmp/cSmOql4G3i" (some pid). 16 my $user=shift; 17 # User who originated mytalk.pl session has $usernum == 1. 18 # User who is responding to mytalk.pl has $usernum == 2. 19 20 my $usernum = $user =~ /^-(\d+)/ ? 2 : 1; 21 my $pid = ($usernum == 1)?$$:$1; 22 STDOUT->autoflush(1); 23 STDIN->blocking(0); 24 25 $handler[$usernum-1]->($pid); 26 exit;
El contenido de usage
es trivial:
110 sub usage { 111 warn "Usage: $pgm user\n"; 112 exit 1; 113 }
El usuario que inicia la comunicación (user1
)
crea los dos FIFOS con los
permisos adecuados.
Después se contacta
mediante el programa write
al otro usuario (user2
).
Para que el mensaje se
reciba es conveniente que el receptor habilite
las escrituras en su terminal mediante el comando mesg .
59 sub user1 { 60 my $pid=shift; 61 62 my $tmpdir = tempdir(); 63 chmod chmod 0755, $tmpdir; 64 65 system("cp $0 $tmpdir/"); 66 67 $inf="$tmpdir/exp0.$pid"; 68 $outf="$tmpdir/exp1.$pid"; 69 70 my $fifocmd = '/usr/bin/mkfifo'; 71 system("$fifocmd $inf") == 0 72 or die "$pgm: could not make fifo 'inf': $?\n"; 73 system("$fifocmd $outf") == 0 74 or unlink($inf), die "$pgm: could not make fifo '$outf': $?\n"; 75 chmod 0666,$inf,$outf; 76 77 print "asking $user to type: $tmpdir/$pgm -$pid $tmpdir\n"; 78 open WQ,"|/usr/bin/write $user" 79 or unlink($inf,$outf), warn "$pgm: write command failed: $!\n"; 80 print WQ "Can we talk? Run: $pgm -$pid $tmpdir\n" or warn "Could'nt warn $user\n"; 81 close WQ; 82 83 open IN,$inf or die "$pgm: read pipe open failed: $!\n"; 84 IN->autoflush(1); 85 IN->blocking(0); 86 87 open OUT,">$outf" or die "$pgm: write pipe open failed: $!\n"; 88 OUT->autoflush(1); 89 90 &dialog(); 91 }
Para hacerlos únicos, los nombres de los FIFOs incluyen el PID del proceso
user1
. Se crea un directorio /tmp/tmpdir/
único.
La ubicación en /tmp
evita la posibilidad de estar trabajando en un sistema
NFS.
lhp@nereida:~/Lperl/src/expect/kibitz$ ls -ltr /tmp/KmTWYAneMz total 4 -rwxr-xr-x 1 lhp lhp 2616 2008-04-18 13:01 mytalk.pl prw-rw-rw- 1 lhp lhp 0 2008-04-18 13:01 exp1.16405 prw-rw-rw- 1 lhp lhp 0 2008-04-18 13:01 exp0.16405
Una llamada de la forma $io->blocking ( [ BOOL ] )
hace que el manejador funcione en modo
no bloqueante si BOOL
es falso. Si no se especifica parámetro
se devolvera el valor actual del parámetro. Si ocurre un error
devuelve undef
y deja el código
de error en $!
.
El usuario receptor (user2
),
siguiendo las instrucciones recibidas,
ejecuta el programa usando como argumento el PID
del proceso user1
y el directorio temporal los cuales
permiten identificar los FIFOs.
El segundo usuario lee desde "/tmpdir/exp1.$pid"
y escribe
en "/tmpdir/exp0.$pid"
mientras que user1
lo hace a la inversa.
El orden en que se abren los ficheros es importante ya que open
puede
atascarse. Si abrimos un pipe para lectura O_RDONLY
el open se bloquea hasta que otro proceso abre el FIFO para escritura
(a menos que se especificara O_NONBLOCK
en la apertura
con sysopen
, en cuyo caso la apertura podrá realizarse con éxito).
Así pues, si en uno de
los procesos se abre primero el de lectura en el otro debe abrirse primero
el de escritura. De manera análoga, si abrimos
un pipe para escritura (O_WRONLY
) la apertura
se bloqueará hasta que el otro proceso abra el FIFO para lectura.
93 sub user2 { 94 my $pid = shift; 95 96 my $tmpdir = shift @ARGV || die "directory wasn't specified\n"; 97 98 $outf="$tmpdir/exp0.$pid"; 99 open OUT,">$outf" or die "$pgm: write pipe open failed: $!\n"; 100 OUT->autoflush(1); 101 102 $inf="$tmpdir/exp1.$pid"; 103 open IN,$inf or die "$pgm: read pipe open failed: $!\n"; 104 IN->autoflush(1); 105 IN->blocking(0); 106 107 &dialog(); 108 }
El diálogo (sub dialog
) hace uso
de comunicaciones sin buffer ( sysread , syswrite , sección
1.7)
y sin bloqueo. Mediante el método
blocking
hemos desactivado el bloqueo en las lecturas de STDIN
(llamadas a STDIN->blocking(0)
y a IN->blocking(0)
).
El módulo IO::Select
nos provee de los mecanismos para saber
que manejadores estan listos para una operación de entrada salida.
Al añadir los manejadores de entrada mediante $s->add(\*STDIN)
y $s->add(\*IN)
indicamos al selector el conjunto de manejadores
que deberá consultar. En la línea (my @read_from = $s->can_read()
)
obtenemos la lista de manejadores que estan preparados para una operación de
lectura. A continuación
procedemos a leer de cada uno de los que
estan preparados.
28 sub dialog { 29 my $s = IO::Select->new(); 30 $s->add(\*STDIN); 31 $s->add(\*IN); 32 33 FOREVER: { 34 my @read_from = $s->can_read(); 35 foreach my $file (@read_from) { 36 if ($file == \*STDIN) { 37 my $bytes = sysread(STDIN, $_, 1024); 38 if (defined($bytes) and ($bytes == 0)) { 39 close(OUT); 40 unlink $inf,$outf; 41 exit 0; 42 } 43 syswrite(OUT,"* $_") if $bytes; 44 } 45 if ($file == \*IN) { 46 my $bytes = sysread(IN, $_, 1024); 47 if (defined($bytes) and ($bytes == 0)) { 48 close(OUT); 49 unlink $inf,$outf; 50 exit 0; 51 } 52 syswrite(STDOUT, $_); 53 } 54 } 55 redo FOREVER; 56 } 57 }
Cuando el manejador esta siendo usado sin bloqueo, la función sysread
devuelve undef
si no hay entrada disponible.
En tal caso la variable $!
contiene el
código de error EWOULDBLOCK (definida en el módulo POSIX
).
Esta situación no debe confundirse con la devolución de un cero,
la cual indica la presencia del final de fichero.
En mas detalle, hay varias posibilidades cuando se llama a
sysread
con un manejador sin bloqueo:
sysread
llena el buffer y devuelve
.
sysread
llena el buffer y devuelve
.
sysread
devuelve undef
y pone
$!
a EWOULDBLOCK
.
sysread
devuelve 0.
sysread
devuelve undef
y pone
$!
al código de error apropiado.
El código típico de una lectura sin buffers ni bloqueo suele tener este aspecto:
my $r = sysread(R, $data, $bytes); if (defined $r) { if ($r > 0) { Lectura con éxito ... } else { close(R); ... código a ejecutar si EOF } } elsif ($! == EWOULDBLOCK) { # Nada en la entrada. Decidir que se hace. } else { die "Error en sysread: $!"; } |
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.