Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/unix/3.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
写入文件描述符的Unix进程在子进程仍处于活动状态时结束(perl)_Perl_Unix_Exec_Fork_Pipe - Fatal编程技术网

写入文件描述符的Unix进程在子进程仍处于活动状态时结束(perl)

写入文件描述符的Unix进程在子进程仍处于活动状态时结束(perl),perl,unix,exec,fork,pipe,Perl,Unix,Exec,Fork,Pipe,所以我有下面的代码,问题是它在所有子进程sort/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 "

所以我有下面的代码,问题是它在所有子进程sort/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";

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

因为本例中的子进程是由system bash命令生成的,所以Perl不知道它们,因此没有简单的方法来处理这个问题,即:您必须猜测这些子进程的PID

我建议您采取两种方法中的一种:要么尝试猜测链中最后一个进程的PID并等待它结束,要么将事件序列分解为单个命令,每个命令生成一个输出,然后将该输出反馈给下一个命令
通过这种方式,您根本不需要执行任何等待操作,“system”命令将为您执行此操作。

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

因为本例中的子进程是由system bash命令生成的,所以Perl不知道它们,因此没有简单的方法来处理这个问题,即:您必须猜测这些子进程的PID

我建议您采取两种方法中的一种:要么尝试猜测链中最后一个进程的PID并等待它结束,要么将事件序列分解为单个命令,每个命令生成一个输出,然后将该输出反馈给下一个命令
这样,您根本不需要进行任何等待,“系统”命令将为您执行此操作。

您将不得不修改您的方法或作弊

Perl的系统等待bash,正如您所知,bash等待管道完成。然而,进程替换运行的命令与bash子进程及其通常的耐心无关。因此:

重做你的方法 找到一种方法使子流程成为真正的管道,而无需流程替换。 通过某种IPC(例如,发送信号、写入字节、非阻塞)将最终处理阶段的信号perl发送到命名管道等。 欺骗 你可以诱使孩子、孙子等实现上述2

让子进程继承管道的写入端,在系统返回后关闭您自己的写入端副本。当所有子体终止时,读取端将选择可读

你需要做一些柔术来让它起作用,因为。比如:

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

这项技术很难防弹,但经常有效。

你将不得不修改你的方法或作弊

Perl的系统等待bash,正如您所知,bash等待管道完成。然而,进程替换运行的命令与bash子进程及其通常的耐心无关。因此:

重做你的方法 找到一种方法使子流程成为真正的管道,而无需流程替换。 通过某种IPC(例如,发送信号、写入字节、非阻塞)将最终处理阶段的信号perl发送到命名管道等。 欺骗 你可以诱使孩子、孙子等实现上述2

让子进程继承管道的写入端,在系统返回后关闭您自己的写入端副本。当所有子体终止时,读取端将选择可读

你需要做一些柔术来让它起作用,因为。比如:

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

这项技术很难做到防弹,但却经常奏效。

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

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);
     }
   }
 }
}

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

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);
     }
   }
 }
}

您可以将流程替换转换为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代码不需要更改


来源:

您可以将进程替换转换为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代码不需要更改


来源:

在我的系统上的bash中运行yes | head-n1000000 | awk'{print rand}>>sort | gzip-9-c>/dev/null会导致错误。首先尝试测试希望在shell中运行的内容。mxedeler从这里开始运行良好。madreblu,你的代码在这里也运行良好。您希望它做什么?omnipotential:如果您检查htop,您会看到sort+gzip在程序打印完成后仍在运行。我希望perl脚本在所有事情都真正完成时打印完成所有子进程都已死亡。简单的Google perl等待子进程的可能副本将为您提供解决方案。在我的系统上的bash中运行yes | head-n 1000000 | awk'{print rand}>>sort | gzip-9-c>/dev/null将导致错误。首先尝试测试希望在shell中运行的内容。mxedeler从这里开始运行良好。madreblu,你的代码在这里也运行良好。您希望它做什么?omnipotential:如果您检查htop,您会看到sort+gzip在程序打印完成后仍在运行。我想让perl脚本优先于
nt done当所有事情都真正完成时,所有的子进程都死了。可能重复一个简单的Google perl wait for子进程会为您提供解决方案。