Дескриптор ввода-вывода для STDOUT в Perl на Windows при разветвлении? - PullRequest
2 голосов
/ 18 января 2011

У меня следующая проблема: Я пытаюсь портировать Perl-скрипт на Windows. Сценарий довольно часто разветвляется и использует этот код для разветвления:

sub sub_fork {
    my ( $subref, @args ) = @_;

    my $fh = new IO::Handle;
    my $pid = open( $fh, "-|" );

    if ( $pid ) { # parent
 return ( $fh, $pid );
    }
    else {
 &$subref( @args );
 exit;
    }
}

Windows не нравится "- |" похоже на то. На самом деле я понятия не имел, что это делает, как Windows-парень, но здесь есть кое-что интересное: http://www.cs.tufts.edu/comp/150PPP/notes/perl_ipc.php (поиск "Необычные открытия") Код часто используется в скрипте, поэтому я хочу заменить подпрограмму на ту, которая работает точно так же, то есть она возвращает $ fh, $ pid, где $ fh - дескриптор stdout дочернего элемента.

1 Ответ

4 голосов
/ 18 января 2011

Эта конструкция используется для межпроцессного взаимодействия , в частности, для создания дескриптора файла, который читает стандартный вывод нового фонового процесса.

my $pid = open my $fh, "-|";
if ($pid == 0) { # child
    print "Hello world\n";
    exit;
}
print <$fh>;       #  Hello world\n

См. perlipc doc по некоторым причинам, почему эта конструкция полезна.

Однако это работает только в системах с "истинной вилкой",что исключает Windows.Обходной путь Windows будет включать использование socketpair для создания некоторых сокетов для IPC (pipe, увы, не подходит и для Windows).Примерно так будет работать:

sub sub_fork {
    my ($subref, @args) = @_;
    use Socket;
    my ($READER, $WRITER);
    socketpair $READER, $WRITER, AF_UNIX, SOCK_STREAM, PF_UNSPEC;
    shutdown($READER,1);    # close write channel for $READER
    shutdown($WRITER,0);    # and read channel for $WRITER
    my $pid = fork();
    if ( $pid ) {
        return ($READER, $pid);
    } else {
        close STDOUT;
        open STDOUT, '>&' . fileno($WRITER);    # dup STDOUT to print to $WRITER

        &$subref(@args);

        # both of these steps are required before you exit the child
        close STDOUT;
        shutdown($WRITER,1);
        exit;
    }
}

Модуль Forks::Super (который я написал) также может решать эту задачу в Windows.

use Forks::Super;
sub sub_fork {
    my ($subref, @args) = @_;
    my $pid = fork { child_fh => 'out' };   # make child's STDOUT available
    if ($pid != 0) {
        return ($Forks::Super::CHILD_STDOUT{$pid}, $pid);
        #alternate: return ($pid->{child_stdout}, $pid);
    } else {
        &$subref(@args);
        exit;
    }
}

или дажеболее кратко

use Forks::Super;
sub sub_fork {
    my ($subref, @args) = @_;
    my $pid = fork { 
                 child_fh => 'out',
                 sub => $subref, args => \@args   # run $subref->(@args) in child
              }; 
    return ($Forks::Super::CHILD_STDOUT{$pid}, $pid);
}
...