2

だから私は、いくつかの有用なタスクを行う子供たちを生み出すプログラムを持っています。次に、最初の子が停止するのを待ってから作業を行う必要がある別の子を生成します。その後、親プログラムは実行を継続し、最後に最後に fork された子プログラムが停止するのを待ちます。

他の子を待つ必要がある子がいないという問題が発生しています。

use strict;
use warnings;
use diagnostics;

my $pid1;
my $child1 = fork();
if ($child1) {
    # parent
    #print "pid is $pid, parent $$\n";
    $pid1 = $child1;
} elsif ($child1 == 0) {
        # child1
        # do something
        sleep 20;
        print "Child1\n";

        exit 0;
} else {
        die "couldnt fork: $!\n";
}

my $pid2;
my $child2 = fork();
if ($child2) {
    # parent
    #print "pid is $pid, parent $$\n";
    $pid2 = $child2;
} elsif ($child2 == 0) {
        # child2
        # wait for child1 to finish
        my $tmp = waitpid($pid1, 0);

        # do something else
        print "Child2\n";

        exit 0;
} else {
        die "couldnt fork: $!\n";
}

# do more stuff

# wait for child2 to finish
my $tmp = waitpid($pid2, 0);

これを行う簡単な方法はありますか?おそらく、最初の子を2番目の子でラップする必要はありませんか?

4

4 に答える 4

2

Unixライクなシステムでは、特定のプロセスはそれ自体の子が死ぬのを待つことしかできません。兄弟、祖先、孫が死ぬのを待つことはできません。

于 2013-01-18T22:38:35.180 に答える
2

子プロセスを 1 つ生成し、そのプロセスにサブ子プロセスを生成させ、それらが終了するのを待ってから最初の子プロセスを続行します。

次に、親にその作業を行わせ、待機する準備ができたら子プロセスを待機させます。

于 2013-01-18T23:47:03.377 に答える
1

perl持っている人や友達がいると仮定すると、semopSystemVセマフォを使用して子間の同期をとることができます。実用的なサンプルプログラムについては、以下を参照してください。

通常のフロントマターから始めます。このコードは、低レベルのセマフォ操作を直接呼び出すのではなく、組み込みの IPC::SysVおよび IPC::Semaphoreモジュールを使用します。

#! /usr/bin/env perl

use strict;
use warnings;

use IPC::Semaphore;
use IPC::SysV qw/ IPC_PRIVATE S_IRUSR S_IWUSR IPC_CREAT /;

このプログラムは、子プロセスを2つの段階に分けます。最初の段階の子は、同期の心配なしに処理を実行して、完了するまで実行されます。これらの多くを任意に持つことができます。

第2段階のプロセスは1つですが、すべて の第1段階の子が終了した後に実行されます。

以下は、単純なプレースホルダーの実装です。

# how many other children the last child must wait for
my $FIRST_STAGE_CHILDREN = 2;

sub first_stage {
  my($id) = @_;

  print "[$$] hello from child $id\n";
  sleep rand 10;
  print "[$$] child $id done\n";
}

sub second_stage {
  print "[$$] hello from second-stage child!\n";
}

第1ステージと第2ステージの間の同期を実装するために、プログラムは、サイズが第1ステージの子の数に等しいセマフォのセットを作成します。第1段階の子が完了すると、プログラムはその子に対応する特定のセマフォを解放します。

my $sem = IPC::Semaphore->new(
  IPC_PRIVATE, $FIRST_STAGE_CHILDREN,
  S_IRUSR | S_IWUSR | IPC_CREAT)
    or die "$0: failed to create semaphore: $!";

後で見るように、第2段階の子供は、セマフォをデクリメントしようとして、兄弟を待ちます。値をゼロから開始することにより、第2ステージの子がこれらのデクリメントを試行すると、OSは子をスリープ状態にします。すべての第1段階の子が終了してセマフォを解放した後でのみ、システムは第2段階の子のブロックを解除します。

# start in blocked state
$sem->setall((0) x $FIRST_STAGE_CHILDREN);

まず、私たちforkは第一段階の子供たちです。この設計では、親プロセスが可能な限り多くの簿記を行います。first_stageこれにより、との定義がsecond_stage単純に保たれます。また、第1ステージの子供がセマフォを解放せずに何らかの形で終了した場合、第2ステージは実行される見込みがありません。

my %kids;
foreach my $id (0 .. $FIRST_STAGE_CHILDREN - 1) {
  my $pid = fork;
  die "$0: fork: $!" unless defined $pid;

  if ($pid) {
    ++$kids{$pid};
  }
  else {
    first_stage $id;
    $sem->op($id, 1, 0);  # release
    exit 0;
  }
}

次に、第2段階の子をフォークします。重要:コードは複数のセマフォに対して操作を実行しますが、これはアトミックに発生します。つまり、すべてのセマフォに対して機能するか、まったく機能しないかのいずれかです。観察可能な状態では、第2段階がすべての第1段階のセマフォよりも少ない数を取得できたように見えます。これは重要なプロパティです。より複雑なシステムでは、無計画なワンジーツーシーの取得と解放はデッドロックを引き起こします。

my $pid = fork;
die "$0: fork: $!" unless defined $pid;

if ($pid) {
  ++$kids{$pid};
}
else {
  # block waiting on all first-stage children
  my @op = map +($_, -1, 0), 0 .. $FIRST_STAGE_CHILDREN - 1;
  $sem->op(@op);

  second_stage;
  exit 0;
}

最後に、親プロセスはすべての子が完了するのを待ちます。

do {
  $pid = waitpid -1, 0;
  print "[$$] reaped $pid\n";
  warn "$0: unknown child $pid" unless delete $kids{$pid};
} while $pid > 0 && keys %kids;

サンプル出力は以下のとおりです。一時停止を確認できるライブを見るのはもっと面白いです。

[18389]子供0からこんにちは
[18390]子供からこんにちは1
[18390]子1が完了しました
[18388]刈り取られた18390
[18389]子0が完了しました
[18391]第二段階の子供からこんにちは!
[18388]刈り取られた18389
[18388]刈り取られた18391
于 2013-01-19T15:30:31.513 に答える
1

これを行う簡単な方法は、 を使用することですForks::Super

use Forks::Super;
my $child1 = fork();
if ($child1 != 0) {
    # ... parent code ...
} else {
    # ... child code ...
    exit;
}

my $child2 = fork { 
    depend_on => $child1, 
    on_busy => 'queue',
    sub => sub {
       # ... code to execute in 2nd child ...
    }
};
# ... more code to run in the parent ...
# ... and at the end of the program:
waitall;

ではForks::Superwaitpidまだ親で呼び出されています (舞台裏で)。しかし、最初の子プロセスが終了Forks::Superすると、バックグラウンドで 2 番目の子プロセスの起動を開始する時期であることがわかります。

于 2013-01-20T01:37:53.340 に答える