信号干扰管道通信的方式有哪些?
What are the ways that signals can interfere with pipe communication?
我对信号一窍不通,对管道也略知一二
来自评论
zdim's answer here
看来信号可能会干扰父子进程之间的管道通信。
有人告诉我,如果您正在使用
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"
信号也可能会中断 I/O 功能,导致 $!
设置为 EINTR
后失败。所以你应该检查该错误并在它发生时重试。
不这样做是难以发现错误的常见来源。
两件事。
在 reader 关闭后写入管道(例如,可能因为另一端的进程退出)导致 SIGPIPE。您可以忽略此信号 ($SIG{PIPE} = 'IGNORE';
),以便改为写入 return 错误 EPIPE
。
在你的例子中,如果你想处理那个错误而不是杀死你的程序,只需添加
$SIG{PIPE} = 'IGNORE';
如果您定义了任何信号处理程序(例如使用 $SIG{...} = sub { ... };
,但不使用 $SIG{...} = 'IGNORE';
或 $SIG{...} = 'DEFAULT';
),long-运行ning 系统调用(例如从文件句柄中读取)可以被信号中断。如果发生这种情况,他们将 return 错误 EINTR
给信号处理程序 运行 的机会。在 Perl 中,您无需执行任何操作,只需重新启动失败的系统调用即可。
在你的情况下,你没有定义信号处理程序,所以这不会影响你。
顺便说一句,即使已知 $rv
未定义,您也会检查 $rv == 0
,并且将数据的长度放在 @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"
我对信号一窍不通,对管道也略知一二
来自评论 zdim's answer here 看来信号可能会干扰父子进程之间的管道通信。
有人告诉我,如果您正在使用
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"
信号也可能会中断 I/O 功能,导致 $!
设置为 EINTR
后失败。所以你应该检查该错误并在它发生时重试。
不这样做是难以发现错误的常见来源。
两件事。
在 reader 关闭后写入管道(例如,可能因为另一端的进程退出)导致 SIGPIPE。您可以忽略此信号 (
$SIG{PIPE} = 'IGNORE';
),以便改为写入 return 错误EPIPE
。在你的例子中,如果你想处理那个错误而不是杀死你的程序,只需添加
$SIG{PIPE} = 'IGNORE';
如果您定义了任何信号处理程序(例如使用
$SIG{...} = sub { ... };
,但不使用$SIG{...} = 'IGNORE';
或$SIG{...} = 'DEFAULT';
),long-运行ning 系统调用(例如从文件句柄中读取)可以被信号中断。如果发生这种情况,他们将 return 错误EINTR
给信号处理程序 运行 的机会。在 Perl 中,您无需执行任何操作,只需重新启动失败的系统调用即可。在你的情况下,你没有定义信号处理程序,所以这不会影响你。
顺便说一句,即使已知 $rv
未定义,您也会检查 $rv == 0
,并且将数据的长度放在 @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"