写入文件描述符的Unix进程在子进程仍处于活动状态时结束(perl)
所以我有下面的代码,问题是它在所有子进程sort/gzip完成之前退出。如何指示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 "
#!/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子进程会为您提供解决方案。