Почему мой родительский процесс не видит вывод дочернего процесса, пока он не завершится? - PullRequest
6 голосов
/ 26 февраля 2010

Рассмотрим следующий скрипт:

use IO::File;
$| = 1;
my ($handle, $pid) = myPipe();
if ($pid == 0) {
  print "$$";
  sleep 5;
  exit;
}

print "child: ".<$handle>."\n";

sub myPipe {
  my $handle = new IO::File();
  my $pid = open($handle, "-|");
  return ($handle, $pid);
}

В этом случае сообщение «child:» не появляется в течение 5 секунд после запуска процесса. Если я уберу вызов сна из разветвленного ребенка, то он сразу напечатает. Почему раздвоенный ребенок должен выйти, чтобы труба вышла к родителю?

Ответы [ 3 ]

12 голосов
/ 26 февраля 2010

Есть две проблемы. Во-первых, дочерний процесс буферизует свой вывод; и во-вторых, родительский процесс использует оператор <>, который блокируется до тех пор, пока не будет доступна полная строка, или до конца файла.

Итак, один из способов получить ожидаемый результат - заставить дочерний процесс закрыть свой поток вывода сразу после записи:

if ($pid == 0) {
    print "$$";
    close STDOUT;
    sleep 5;
    exit;
}

Другой способ - добавить новую строку в вывод дочернего процесса, а затем очистить поток:

if ($pid == 0) {
    print "$$\n";
    STDOUT->flush;  # "close STDOUT;" will work too, of course
    sleep 5;
    exit;
}

Сброс необходим, потому что каналы (как правило) не буферизуются, а не буферизуются по линии, как потоки, подключенные к терминалу.

Третий вариант - установить для потока вывода дочернего процесса значение autoflush:

if ($pid == 0) {
    $| = 1;
    print "$$\n";
    sleep 5;
    exit;
}
3 голосов
/ 26 февраля 2010

В некоторых (большинстве?) Системах каналы по умолчанию используют буферизацию ввода / вывода. Положить

$handle->autoflush(1);

оператор в вашей myPipe функции.

Но даже когда буферизация отключена, Perl все равно не сбрасывается, кроме как после новой строки. Таким образом, вы также можете захотеть, чтобы ваш дочерний процесс включил новую строку в вывод.


Обновление: Тестируя ваш код (Cygwin, perl 5.10.0, YMMV), я вижу, что проблема заключается в отсутствии новой строки в выходных данных, а не в том, явно ли включена автозапуск при передаче создан.

2 голосов
/ 26 февраля 2010

Промывка трубы не происходит по какому-либо фиксированному графику. Вы можете принудительно сбросить канал только двумя способами: выйти из дочернего процесса (что вы сейчас делаете) или явно вызвать flush. Вы можете заставить вашу ручку сбрасываться в perl, выполнив любое из следующих действий:

  • Добавление \n в конец дочернего сообщения, которое (обычно) вызывает сброс канала
  • Установка $| в 1, что приводит к автоматическому сбросу текущего выбранного дескриптора файла
  • Использование IO::Handle и вызов $handle->flush.
  • Использование IO::Handle и настройка $handle->autoflush = 1
...