Почему IPC::Open3 блокируется?

Я пошел через документацию open3и вот часть, которую я не мог понять:

Если вы попытаетесь прочитать из дочернего модуля записи stdout и их stderr писатель, у вас будут проблемы с блокировкой, а значит, вы захотите используйте select() или IO::Select, что означает, что вам лучше всего использовать sysread() вместо readline() для обычных вещей.

Это очень опасно, так как вы можете заблокировать навсегда. Предполагается, что это собирается поговорить с чем-то вроде bc, и писать ему, и читать от него. Это предположительно безопасно, потому что вы «знаете», что такие команды, как bc будет читать строку за раз и выводить строку за раз. Программы однако сортировка, которая сначала читает весь входной поток, вполне может вызвать взаимоблокировку.

Итак, я попробовал open3в надежде узнать его получше. Вот первая попытка:

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

Интересно отметить, что здесь я должен инициализировать $err.

Во всяком случае, это просто зависает, когда я execute("sort $some_file");учитывая, что $some_fileявляется текстовым файлом, содержащим более 4096 символов (ограничения для моей машины) .

Затем я просмотрел этотFAQ, и ниже была моя новая версия execute:

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() {
        print "[ERROR] : $_" while();
        die;
    }
    print "[OUTPUT]: $_" while ();
}

Команда sortтеперь выполняется нормально, но я не могу понять, почему.

[Обновление]Прочитав ответ @tchrist, я прочитал IO::Selectи, погуглив еще, нашел эту версию 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);
}

Это работает нормально, и теперь некоторые вещи стали яснее. Но общая картина все еще немного туманна.

Итак, мои вопросы:

  1. Что не так с hung_execute?
  2. Думаю, good_executeработает из-за >&в вызове open3. Но почему и как?
  3. Кроме того, good_executeне работал, когда я использовал лексические переменные ( my $outвместо OUT) для файловых дескрипторов. Это дало эту ошибку: open3: open(GLOB(0x610920), >&main::OUT) failed: Invalid arguments. Почему так?
  4. Кажется, что только один из файловых дескрипторов может писать в данный момент времени, и если я отбрасываю дескриптор, удерживающий ресурс, другие дескрипторы продолжают ждать. Раньше я думал, что STDERR и STDOUT были независимыми потоками и не делили никаких ресурсов. Я предполагаю, что мое понимание немного ошибочно здесь. Пожалуйста, дайте мне несколько советов по этому поводу.

9
задан Unos 5 April 2012 в 15:11
поделиться