What are the ways that signals can interfere with pipe communication?

时间:2018-02-01 18:17:05

标签: perl pipe ipc

I don't know anything about signals, and only a little about pipes.

From the comments on zdim's answer here it seems that signals may interfere with pipe communication between parent and child processes.

I was told that, if you're using IO::Select and sysread, then the exit of a child process could somehow mess up the behavior of IO::Select::can_read, especially if there are multiple child processes.

Please describe how to account for signals when using pipes? The below code is an example where signals are not accounted for.

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 个答案:

答案 0 :(得分:4)

两件事。

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

    在您的情况下,如果您想处理该错误而不是让您的程序被终止,只需添加

    即可
    $SIG{PIPE} = 'IGNORE';
    
  2. 如果您定义了任何信号处理程序(例如使用$SIG{...} = sub { ... };,而不是$SIG{...} = 'IGNORE';$SIG{...} = 'DEFAULT';),则长时间运行的系统调用(例如从文件句柄读取)可以被信号打断。如果发生这种情况,它们将返回错误EINTR以使信号处理程序有机会运行。在Perl中,除了重新启动失败的系统调用之外,您不必执行任何操作。

    在您的情况下,您没有定义信号处理程序,因此这不会影响您。

  3. 顺便说一下,即使知道$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"
    

答案 1 :(得分:2)

信号也可能会中断I / O功能,导致$!设置为EINTR时失败。因此,您应检查该错误,并在发生时重试。

不这样做是很难找到错误的常见原因。