perl - 信号干扰管道通信的方式有哪些?

标签 perl pipe ipc

我对信号一窍不通,对管道也略知一二。

来自评论 zdim's answer here 看起来信号可能会干扰父进程和子进程之间的管道通信。

有人告诉我,如果你正在使用 IO::Selectsysread , 然后退出一个子进程 可能会以某种方式搞乱 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"

最佳答案

两件事。

  1. 在读取器关闭后写入管道(例如,可能因为另一端的进程退出)导致 SIGPIPE。您可以忽略此信号 ($SIG{PIPE} = 'IGNORE';),以便写入返回错误 EPIPE

    在你的例子中,如果你想处理那个错误而不是杀死你的程序,只需添加

    $SIG{PIPE} = 'IGNORE';
    
  2. 如果您定义了任何信号处理程序(例如使用 $SIG{...} = sub { ... };,但不是 $SIG{... } = 'IGNORE';$SIG{...} = 'DEFAULT';),长时间运行的系统调用(例如从文件句柄读取)可以被中断信号。如果发生这种情况,它们将返回错误 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"

关于perl - 信号干扰管道通信的方式有哪些?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48569304/

相关文章:

c - 使用 perl 更新 C 文件中的宏

perl - 为 Perl 5.18.xx 安装 DBI-mysql

perl - 如何在 ubuntu 18.04 LTS 中安装 perl 5.10.1?

bash - 如何将所有输出重定向到/dev/null?

haskell - 如何以管道方式混合 Haskell monadic 和纯过滤器?

perl - 尝试重新加载 module.pm 中止。编译失败

sql - 从竖线分隔的列中选择数据

无法在 C 中的管道中写入两次

ruby - Nginx与后端服务器之间的IPC机制是怎样的?

c - 获取猫 :/dev/mydevice1: Invalid argument as output when trying to communicate in driver