Fui a través de la documentación de open3 y aquí está la parte que no podía comprender:¿Por qué IPC :: Open3 se bloquea?
If you try to read from the child's stdout writer and their stderr writer, you'll have problems with blocking, which means you'll want to use select() or the IO::Select, which means you'd best use sysread() instead of readline() for normal stuff.
This is very dangerous, as you may block forever. It assumes it's going to talk to something like bc, both writing to it and reading from it. This is presumably safe because you "know" that commands like bc will read a line at a time and output a line at a time. Programs like sort that read their entire input stream first, however, are quite apt to cause deadlock.
Así que probé open3
la esperanza de conocerlo mejor. Este es el primer intento:
sub hung_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
if(<$err>) {
print "[ERROR] : $_" while(<$err>);
die;
}
print "[OUTPUT]: $_" while (<$out>);
}
Es interesante observar que debo inicializar $err
aquí.
De todos modos, esto simplemente se bloquea cuando I execute("sort $some_file");
dado que $some_file
es un archivo de texto que contiene más de 4096 caracteres (límites para mi máquina).
Entonces miré en this Preguntas y abajo era mi nueva versión de ejecutar:
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $in = gensym();
#---------------------------------------------------
# using $in, $out doesn't work. it expects a glob?
local *OUT = IO::File->new_tmpfile;
local *ERR = IO::File->new_tmpfile;
my $pid = open3($in, ">&OUT", ">&ERR", $cmd);
print "[PID]: $pid\n";
waitpid($pid, 0);
seek $_, 0, 0 for \*OUT, \*ERR;
if(<ERR>) {
print "[ERROR] : $_" while(<ERR>);
die;
}
print "[OUTPUT]: $_" while (<OUT>);
}
El comando sort
ejecuta bien ahora, pero no puedo entender por qué.
[Actualización] Después de leer la respuesta de @ tchrist, leí IO::Select
, y después de un poco más de google, han llegado con esta versión de execute
:
sub good_execute {
my($cmd) = @_;
print "[COMMAND]: $cmd\n";
my $pid = open3(my $in, my $out, my $err = gensym(), $cmd);
print "[PID]: $pid\n";
my $sel = new IO::Select;
$sel->add($out, $err);
while(my @fhs = $sel->can_read) {
foreach my $fh (@fhs) {
my $line = <$fh>;
unless(defined $line) {
$sel->remove($fh);
next;
}
if($fh == $out) {
print "[OUTPUT]: $line";
}elsif($fh == $err) {
print "[ERROR] : $line";
}else{
die "[ERROR]: This should never execute!";
}
}
}
waitpid($pid, 0);
}
Esto está trabajando muy bien, y algunas cosas se han vuelto más claros ahora. Pero la imagen general todavía es un poco confusa.
Así que mis preguntas son:
- ¿Qué pasa con
hung_execute
? - Supongo que
good_execute
funciona debido a>&
en la llamada open3. Pero, ¿por qué y cómo? - Además,
good_execute
no funcionó cuando usé variables léxicas (my $out
en lugar deOUT
) para manejadores de archivos. Dio este error:open3: open(GLOB(0x610920), >&main::OUT) failed: Invalid argument
. ¿Porque? - Parece que solo uno de los manejadores de archivo puede escribir en un momento determinado, y si descarto el controlador que se queda en el recurso, los otros manejadores siguen esperando. Solía pensar que STDERR y STDOUT eran transmisiones independientes y no compartían ningún recurso. Supongo que mi entendimiento es un poco defectuoso aquí. Por favor, dame algunos consejos sobre esto también.
He leído en el módulo 'IO :: Select', y he actualizado mi pregunta ... – Unos
@Unos Usted tiene muchas preguntas. Se supone que debes hacer solo una pregunta. Ya respondí a los iniciales, pero vuelves a preguntar las mismas cosas como si no te hubieras dado cuenta. Supongo que responder todas las preguntas nuevas requiere un párrafo o tres para casi cada línea de tu código en cada programa. Eso es mucho trabajo para pedirle a alguien, ciertamente más de una hora y lo más probable es que trabaje tres horas de trabajo gratis. Simplemente no tengo ese tiempo hoy. Por favor, estudie lo que ya dije, porque no veo que se haya hundido. – tchrist
Hola, @tchrist, no quería irritarte. No eliminé mis preguntas anteriores después de la actualización porque pensé que las respuestas podrían perder contexto si lo hiciera. Definitivamente estudiaré esto con más detalle. – Unos