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

Posted

技术标签:

【中文标题】信号干扰管道通信的方式都有哪些?【英文标题】:What are the ways that signals can interfere with pipe communication?信号干扰管道通信的方式有哪些? 【发布时间】:2018-02-01 18:17:05 【问题描述】:

我对信号一无所知,对管道只有一点了解。

从 cmets 开始 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"

【问题讨论】:

再一次,您是否愿意为此使用管道?使用用于进程间通信的文件,您将减少容量、死锁、信号和可移植性方面的麻烦。和调试(在程序结束时,您可以检查文件并查看它是否包含您期望的内容)。 我尝试使用管道,因为看起来我真的应该学习它们如何工作才能成为一名优秀的工程师。但相比之下,它确实显得过于复杂。我觉得我正在通过一个良好的设置取得进展,但我还没有达到我认为它应该足够可靠的舒适点 我相信管道也会快很多 但是是的,也许我会放弃这项工作:( 如果你对 IPC 不满意,那么切换到使用文件不会有太大帮助:会出现许多相同的问题,最后你必须意识到运行的后果在共享资源的同时并行处理。我假设你已经阅读并吸收了perldoc perlipc? 【参考方案1】:

两件事。

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

    在你的情况下,如果你想处理那个错误而不是让你的程序被杀死,只需添加

    $SIGPIPE = 'IGNORE';
    

    如果您定义了任何信号处理程序(例如使用$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"

【讨论】:

谢谢,这提供了我所缺少的更多上下文。我现在明白中断的发生是为了让我可能定义的信号处理程序可以继续。 有一点我不明白:写怎么会返回EPIPE?你的意思是写条件本身的返回值是一个字符串EPIPE?那样会更好吗? 让我们continue this discussion in chat. @zdim 因为您正在对您的帖子进行更正...我认为当您知道只有一个句柄时使用foreach my $handle (@ready) 循环句柄是错误的。特别是,由于您在看到关闭句柄的那一刻调用命令last READ;,因此代码甚至无法使用多个句柄。 @ikegami,可以在帖子中详细说明的一件事:如果我们遇到警告(未定义 $rv),为什么我们不尝试重新读取句柄。不能有可恢复的错误吗?也许您向 zdim 解释了这一点,但现在 cmets 已被删除。【参考方案2】:

信号也可能会中断 I/O 功能,从而导致 $! 设置为 EINTR 时失败。所以你应该检查那个错误并在它发生时重试。

不这样做是很难找到错误的常见来源。

【讨论】:

以上是关于信号干扰管道通信的方式都有哪些?的主要内容,如果未能解决你的问题,请参考以下文章

在windows操作系统中进程通信的方式都有哪些

Linux 进程间通信方式都有哪些

Linux进程间通信的方式都有哪些

操作系统之 进程间通信的方式都有哪些

管道通信为啥比共享内存效率低

Linux系统编程-进程间通信(管道)