2009-10-20 2 views
6

У меня есть скрипт Perl, который использует внешний инструмент (cleartool) для сбора информации о списке файлов. Я хочу использовать IPC, чтобы избежать порождения нового процесса для каждого файла:Как сделать неблокирующее IPC, прочитанное в Windows?

use IPC::Open2; 
my ($cin, $cout); 
my $child = open2($cout, $cin, 'cleartool'); 

Команды, которые возвращают одиночные линии работают хорошо. например

print $cin "describe -short $file\n"; 
my $description = <$cout>; 

Команды, которые возвращают несколько строк у меня в тупик для того, как потреблять весь ответ без получения подвешивали блокированию чтения:

print $cin "lshistory $file\n"; 
# read and process $cout... 

Я попытался установить указатель файла для неблокирующая читает через fcntl:

use Fcntl; 
my $flags = ''; 
fcntl($cout, F_GETFL, $flags); 
$flags |= O_NONBLOCK; 
fcntl($cout, F_SETFL, $flags); 

но Fcntl умирает с сообщением «Ваш поставщик не определен Fcntl макрос F_GETFL.»

Я пробовал использовать IO :: Ручка для установки $cout->blocking(0), но это не удалось (оно возвращает undef и устанавливает $! в «Неизвестная ошибка»).

Я пытался использовать select, чтобы определить, есть ли имеющиеся данные, прежде чем читать:

my $rfd = ''; 
vec($rfd, fileno($cout), 1) = 1; 
while (select($rfd, undef, undef, 0) >= 0) { 
    my $n = read($cout, $buffer, 1024); 
    print "Read $n bytes\n"; 
    # do something with $buffer... 
} 

но висит никогда не читал ничего. Кто-нибудь знает, как сделать эту работу (в Windows)?

+0

Что вы планируете делать, не блокируя IO? – jrockway

+0

Я хочу читать до тех пор, пока не перестану получать данные, которые, казалось бы, были бы более надежными, чем попытка определить, что я дошел до конца, проанализировав данные, которые я уже получил. (В данных нет индикатора «это последняя строка».) –

+0

Поиск вокруг связанной проблемы, я нашел это фиксацию на GitHub, который, кажется, вызывает черную магию для создания неблокирующего сокета: https: // github .com/kthakore/frozen-bubble/commit/735dd2307455e32c69827e013992c2d022cb7347 –

ответ

5

select only works on sockets в Windows. Похоже, что IPC :: OpenX использует обычные дескрипторы файлов, поэтому вы не сможете использовать select с создаваемыми ручками.

Если вам не нужен тайм-аут/обнаружение активности, которую вы выбираете, вы можете настроить ручки неблокирующими и просто читать или писать в соответствии с нормальным.

Если вам нужно больше нюансированного управления, IPC::Run может работать хорошо для вас.

Вы также можете посмотреть на создание socketpair и использовать эти ручки с дочерними процессами. Более новая поддержка perls (5.8 и выше) socketpair эмуляции в Windows с использованием сокетов TCP.

Если вы пытаетесь клонировать STDOUT и STDERR для программы, которая запускается без консоли (т.е. она запускается с помощью wperl, а не perl), вы не сможете получать данные через STDIO.

На практике это было огромной болью для меня в нескольких проектах. Я нашел работу лучше всего, чтобы написать дочерний процесс для подключения к родительскому серверу через TCP. Если вы не контролируете дочерние процессы, посмотрите на IPC::Run или socketpair.

+1

Да, Windows сосет. При запуске IPC :: Run выполняет трюк сокета, который вы упоминаете. – ephemient

+0

Как я писал в вопросе, мне не удалось сделать ручку неблокирующей через Fcntl или IO :: Handle. Если вы знаете способ сделать это (работает в Windows), пожалуйста, поделитесь им. –

+0

@Michael: Запустите блокировку ввода-вывода в отдельном процессе. Общайтесь взад и вперед с основным процессом, используя сокеты. Детский процесс может блокироваться, но основной процесс может выполнять неблокирующий ввод-вывод в коммуникационных сокетах. Это то, что предлагает daotoad с «socketpair». – ephemient

0

Неблокирующийся IO сильно зависит от Windows. В этом случае, вы можете отправить вывод cleartool в обычный файл и использовать seek сбросить флаг ВФ на файл каждый раз, когда из чтения из файла:

my($cin, $cout); 
my $somefile = "some/file"; 
open($cin, "| cleartool > $somefile"); 
open($cout, '<', $somefile); 

... 

print $cin "$command_for_cleartool\n"; 
# if necessary, wait until cleartool finishes with new output 
seek $cout, 0, 1;  # clear eof condition from previous read 
my @cleartool_output = <$cout>; # capture recent output 
... process output ... 

Хотя это, вероятно, не работает, что хорошо, если cleartool буферизирует его выход.

1

Другой kludge должен использовать sysread с большим или маловероятным размером буфера.

print $cin "$command_for_cleartool\n"; 
    my ($description, $maxlen, $buffer) = ("", 65336); 
    while (my $n = sysread $cout, $buffer, $maxlen) { 
     $description .= $buffer; 
     last if $n < $maxlen; 
    } 
    ... do something with $description ... 

sysread будет висеть, если ровно 0 байт ввода, ожидающие чтения. Таким образом, код выше будет зависать, если cleartool производит точно несколько кратных 65336 байт. Если вы знаете хорошую верхнюю границу по размеру выхода из программы, вы можете использовать это значение для $maxlen выше. В противном случае вы могли бы выбрать большое и маловероятное число и молиться ...