perl父进程挂起等待子进程读取标准输入

时间:2016-03-06 21:21:07

标签: perl io

我有一个模拟tee命令的perl脚本,因此我可以将输出写入终端和日志文件。它的工作原理是这样的(错误检查& c省略)。

$pid = open(STDOUT, '-|');
# Above is perl magic that forks a process and sets up a pipe with the
# child's STDIN being one end of the pipe and the parent's STDOUT (in
# this case) being the other.
if ($pid == 0)
{
    # Child. 
    # Open log file
    while (<STDIN>)
    {
       # print to STDOUT and log file
    }
    #close log files
    exit;
}
# parent
open STDERR, '>&STDOUT';
# do lots of system("...") calls
close STDERR;
close STDOUT;
exit;

这有时会挂起,并且总是如果你看一下所述进程的进程和堆栈,父进程就会挂在其中一个关闭状态,等待孩子退出,而孩子正在挂着从文件中读取的东西(必须是STDIN,因为没有其他文件。)

我对如何处理这件事感到茫然。如果你从没有附加到控制台的shell运行程序似乎会发生这个问题 - 在普通shell中运行脚本工作正常 - 并且最近在该脚本中更改的唯一代码是添加了打开/关闭文件只是为了触摸它(它是在脚本到达'tee'代码之前)。

有没有人之前遇到过这样的问题和/或有什么建议可以解决这个问题?感谢。

2 个答案:

答案 0 :(得分:0)

<强>更新

我无法重现违规行为。对我来说,它从终端和cron都按预期运行。 (如果是后台,它将在fd 1,2时被暂停。)鉴于控制台重定向时出现问题,这就是我想要尝试的事情

1)在子级和父级中明确关闭缓冲

2)在while循环

之后显式关闭STDIN

3)陷阱SIGPIPE以查看是否出现了问题

*)其他一些代码实际上是否被绊倒了?


以下是关于首先发布的变通方法的想法。

似乎问题在于STDOUT使用重定向控制台会发生什么,然后孩子可以或不能用它做什么。我(还)不知道那是什么,但与此同时,我认为以下几行中的某些内容至少应该避免这个问题。

如果没有STDOUT,孩子可以将tty的输出写入另一个文件。此文件可以是父级的重定向(如果已知,附加到),或者父级稍后将加入的另一个显式打开的文件。它变得凌乱,粗糙,它改变了设计,但它可能使孩子退出(通过不处理可能是问题根源的STDOUT)。

或者,父母可以控制这一点,在没有tty的情况下以不同的方式打开记录器子,这将完全避开情况,但离设计更远。

这些都没有解决问题,但可以避免它。

作为参考,这是从cmdline和cron

之外的行为
$| = 1;
$logfile = '/path/logfile.out';

$pid = open(STDOUT, '|-');
if ($pid == 0) {
    # $| = 1;
    open $log_fh, '>', $logfile;
    while (<STDIN>) {
        print $_;
        print $log_fh $_;
    }
    # close STDIN;
    close $log_fh;
    exit;
}
open STDERR, '>&STDOUT';

print "After the fork.\n";
warn "A warn --";

close STDERR;
close STDOUT;
exit;

答案 1 :(得分:0)

好吧,经过一些实验,似乎开放STDOUT直接看起来至少是部分原因。我的代码现在是这样的:

$pid = open($handle, '|-');
if ($pid == 0)
{
    # Child. 
    # Open log file
    while (<STDIN>)
    {
       # print to STDOUT and log file
    }
    #close log files
    exit;
}
# parent
open my $oldout, '>&STDOUT';
open my $olderr, '>&STDERR';
open STDOUT, '>&', $handle;
open STDERR, '>&', $handle;
# do lots of system("...") calls
open STDOUT, '>&', $oldout;
open STDERR, '>&', $olderr;
close $handle or die "Log child exited unexpectedly: $!\n";
exit;

如果没别的话,看起来更干净(但仍然比我更糟糕,因为我不知道如果这些重复有任何错误该怎么办)。但是我仍然不清楚为什么在代码中更早地打开和关闭句柄会对这一点产生如此大的影响。