【问题标题】:Unix process writing to a file descriptor ends while the children are still alive (perl)Unix进程写入文件描述符在孩子还活着时结束(perl)
【发布时间】:2012-06-03 04:02:27
【问题描述】:

所以我有以下代码,问题是它在所有子进程(排序/gzip)完成之前就退出了。如何指示 Perl 等待所有后代进程?

#!/usr/bin/perl


use strict;
use warnings;

sub systemBash {
  my $cmd = shift;
  my @args = ( "bash", "-c", $cmd );
  print "command ".$cmd."\n";
  system(@args);
  if($? != 0){
    die "Command ".Dumper(@args)." failed";
  }
}
print "start";
systemBash("yes |head -n 1000000|awk '{print rand()}' > >(sort |gzip -9 -c > /dev/null)");
print "done";

【问题讨论】:

  • 在我系统的 bash 中运行 "yes |head -n 1000000|awk '{print rand()}' > >(sort |gzip -9 -c > /dev/null)" 导致一个错误。首先尝试测试您想在 shell 中运行的内容。
  • mxedeler,从这里运行良好。 madreblu,您的代码似乎也从这里运行良好。你期待它做什么?
  • OmnipotentEntity:如果您检查 htop,您会看到在程序打印“完成”后 sort + gzip 仍在运行。我希望 perl 脚本在一切真正完成时打印完成(所有后代都死了)。
  • 一个简单的谷歌“perl 等待子进程”会给你解决方案。

标签: perl unix exec fork pipe


【解决方案1】:

为了让 Perl 等待一个进程,它首先需要知道它应该等待的进程的 PID。

因为这个例子中的子进程是由系统产生的(bash 命令),Perl 不知道它们,所以没有简单的方法来处理它,即:你必须猜测那些子进程的 PID-进程。

我建议您采用以下两种方法之一:尝试猜测链中最后一个进程的 PID 并等待它结束,或者将事件序列分解为单个命令,每个命令都会产生一个输出后来“喂”到下一个命令 (这样您根本不需要等待 - 'system' 命令会为您完成)。

【讨论】:

    【解决方案2】:

    你将不得不重新设计你的方法或作弊。

    Perl 的system() 等待bash,它(如您所知)等待您的管道完成。但是,进程替换会从您的 bash 孩子及其通常的耐心运行“离婚”命令。所以:

    重新设计你的方法

    1. 找到一种方法,使子流程成为真正的管道,无需流程替换。
    2. 通过某种 IPC 让最后处理阶段的信号 perl(例如,发送信号、写入字节、非阻塞、到命名管道等)

    作弊

    您可以欺骗孩子、孙子等以影响上面的#2。

    让子进程继承pipe()的写端,在system()返回后关闭你自己的写端副本。当所有后代终止时,读取端将选择可读。

    由于pipe()d filehandles are typically subject to $^F,您需要做一些扭曲才能使其工作。比如:

    use POSIX qw(F_SETFD);
    
    ...
      # XXX - error checking omitted
      pipe(my $rd, my $wr);
      fcntl($wr, F_SETFD, 0);
    
      system(...);
      close($wr);
    
      readline($rd); # Will block until all inherited copies of $wr are closed
    

    这种技术几乎不是万无一失的,但经常奏效。

    【讨论】:

      【解决方案3】:

      我写了以下可怕的代码来解决这个问题:

      sub findAllPIDs{
       my ($firstPID) = @_;
       my %hashPID;
       $hashPID{$firstPID}=1;
       my $found;
       do{
         $found=0;
         foreach my $key (keys %hashPID){
           my $cmd="ps -o pid --ppid ".$key;
           my $out=`$cmd`;
           foreach my $line (split("\n",$out)){
             $line =~ s/^\s+//;
             $line =~ s/\s+$//;
             if($line ne "PID"){
               if(!(exists $hashPID{$line})){
                 $hashPID{$line}=1;
                 $found=1;
               }
             }
           }
         }
      
       }while($found);
       return (keys %hashPID);
      }
      
      sub poll{
       my $proc=shift;
       while(1){
         my $cmd="ps  -o s --pid $proc";
         my $out=`$cmd`;
         my $found=0;
         my @arr=split("\n",$out);
         shift(@arr);
         foreach my $line (@arr){
           $line =~ s/^\s+//;
           $line =~ s/\s+$//;
           if($line ne "Z"  ){
             $found=1;
           }
         }
         if(!$found){
           return $proc;
         }
         sleep(2);
       }
      }
      
      sub systemBash {
       my $cmd = shift;
       my @args = ( "bash", "-c", $cmd );
      
       print "running ".$cmd."\n";
       if(!$fakeRun){
         my $pid=fork();
         if($pid == 0){
           exec(@args);
         }else{
           sleep(1);
           my @array=findAllPIDs($pid);
           foreach my $pid (@array){
             my $code =poll($pid);
           }
         }
       }
      }
      

      【讨论】:

        【解决方案4】:

        您可以将您的进程替换转换为 FIFO,并明确等待它完成。

        yes |head -n 1000000|awk '{print rand()}' > >(sort |gzip -9 -c > /dev/null)
        

        变成

        mkfifo tmp-fifo; { sort |gzip -9 -c > /dev/null; } < tmp-fifo & pid=$! ; yes |head -n 1000000|awk '{print rand()}' > tmp-fifo; wait $pid; rm tmp-fifo
        

        Perl 代码不需要更改。

        来源:http://lists.gnu.org/archive/html/bug-bash/2011-11/msg00137.html

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2019-06-20
          • 2017-07-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2011-10-30
          相关资源
          最近更新 更多