Каковы способы, которыми сигналы могут вмешиваться в коммуникацию по трубам?
Я ничего не знаю о сигналах, и только немного о трубах.
Из комментариев на ответ здима здесь похоже, что сигналы могут мешать каналу связи между родительскими и дочерними процессами.
Мне сказали, что, если вы используете
IO::Select
и
sysread
,
затем выход из дочернего процесса
может как-то испортить поведение IO::Select::can_read
,
особенно если имеется несколько дочерних процессов.
Пожалуйста, опишите, как учитывать сигналы при использовании труб? Приведенный ниже код является примером, в котором сигналы не учитываются.
use warnings;
use strict;
use feature 'say';
use Time::HiRes qw(sleep);
use IO::Select;
my $sel = IO::Select->new;
pipe my $rd, my $wr;
$sel->add($rd);
my $pid = fork // die "Can't fork: $!"; #/
if ( $pid == 0 ) { # Child code
close $rd;
$wr->autoflush;
for ( 1..4 ) {
sleep 1;
say "tsending data";
say $wr 'a' x ( 120 * 1024 );
}
say "tClosing writer and exiting";
close $wr;
exit;
}
# Parent code
close $wr;
say "Forked and will read from $pid";
my @recd;
READ:
while ( 1 ) {
if ( my @ready = $sel->can_read(0) ) { # beware of signals
foreach my $handle (@ready) {
my $buff;
my $rv = sysread $handle, $buff, ( 64 * 1024 );
warn "Error reading: $!" if not defined $rv;
if ( defined $buff and $rv != 0 ) {
say "Got ", length $buff, " characters";
push @recd, length $buff;
}
last READ if $rv == 0;
}
}
else {
say "Doing else ... ";
sleep 0.5;
}
}
close $rd;
my $gone = waitpid $pid, 0;
say "Reaped pid $gone";
say "Have data: @recd"
2 ответа:
Две вещи.
- Запись в трубу после закрытия считывателя (например, возможно, потому, что процесс на другом конце вышел) приводит к СИГПАЙПУ. Вы можете проигнорировать этот сигнал (
$SIG{PIPE} = 'IGNORE';
), чтобы вместо него получить запись для возврата ошибкиEPIPE
.В вашем случае, если вы хотите обработать эту ошибку вместо того, чтобы ваша программа была убита, просто добавьте
$SIG{PIPE} = 'IGNORE';
Если у вас есть определенный обработчик сигнала (например, используя
В вашем случае у вас нет определенных обработчиков сигналов, поэтому это не влияет на вас.$SIG{...} = sub { ... };
, но не$SIG{...} = 'IGNORE';
или$SIG{...} = 'DEFAULT';
), длительные системные вызовы (например, чтение из дескриптора файла) могут быть прерваны сигналом. Если это произойдет, они вернутся с ошибкойEINTR
, чтобы дать обработчику сигнала шанс запустить. В Perl вам не нужно ничего делать, кроме перезапуска системного вызова, который не удался.
Кстати, вы проверяете
$rv == 0
даже тогда, когда$rv
, как известно, не определено, и вы размещаете длину данные в@recd
вместо самих данных. На самом деле, нет особого смысла использовать массив там вообще. Заменитьmy @recd; ... my $rv = sysread $handle, $buff, ( 64 * 1024 ); warn "Error reading: $!" if not defined $rv; if ( defined $buff and $rv != 0 ) { say "Got ", length $buff, " characters"; push @recd, length $buff; } last READ if $rv == 0; ... say "Have data: @recd"
С
my $buf = ''; ... my $received = sysread($handle, $buf, 64 * 1024, length($buf)); warn "Error reading: $!" if !defined($received); last if !$received; say "Got $received characters"; ... say "Have data: $buf"