Почему мой родительский процесс не видит вывода ребенка, пока он не выходит?

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

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);
}

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

7
задан jrockway 26 February 2010 в 22:44
поделиться

3 ответа

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

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

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;
}

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

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

if ($pid == 0) {
    $| = 1;
    print "$$\n";
    sleep 5;
    exit;
}
13
ответ дан 6 December 2019 в 09:59
поделиться

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

$handle->autoflush(1);

в свою функцию myPipe .

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


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

3
ответ дан 6 December 2019 в 09:59
поделиться

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

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

Похожие вопросы: