14

fork()次の/SIGCHLD疑似コードを検討してください。

  // main program excerpt
    for (;;) {
      if ( is_time_to_make_babies ) {

        pid = fork();
        if (pid == -1) {
          /* fail */
        } else if (pid == 0) {
          /* child stuff */
          print "child started"
          exit
        } else {
          /* parent stuff */
          print "parent forked new child ", pid
          children.add(pid);
        }

      }
    }

  // SIGCHLD handler
  sigchld_handler(signo) {
    while ( (pid = wait(status, WNOHANG)) > 0 ) {
      print "parent caught SIGCHLD from ", pid
      children.remove(pid);
    }
  }

上記の例では、競合状態があります。/* child stuff */" " が開始する前に " " が終了する可能性/* parent stuff */があります。その結果、子の pid が終了後に子のリストに追加され、決して削除されない可能性があります。アプリが終了する時が来ると、親は既に完成した子が完成するのを際限なく待ちます。

これに対抗するために考えられる解決策の 1 つは、 と の 2 つのリストを作成することstarted_childrenですfinished_children。今追加しstarted_childrenているのと同じ場所に追加しchildrenます。しかし、シグナル ハンドラでは、 fromchildrenを削除する代わりに、 to を追加finished_childrenます。started_childrenアプリが終了すると、親はとの差finished_childrenがゼロになるまで待つことができます。

私が考えることができる別の可能な解決策は、共有メモリを使用することです。たとえば、親の子のリストを共有し、子.add.remove自分自身を許可しますか? しかし、私はこれについてあまり知りません。

編集: 最初に頭に浮かんだ別の解決策は、単純にsleep(1)先頭に a を追加する/* child stuff */ことですが、それは私には変なにおいがするので、省略しました。また、それが 100% 修正されるかどうかもわかりません。

では、この競合状態をどのように修正しますか? また、これの定評のあるおすすめパターンがあれば教えてください!

ありがとう。

4

4 に答える 4

16

fork()最も簡単な解決策は、前にSIGCHLD シグナルをブロックsigprocmask()し、pid を処理した後に親コードでブロックを解除することです。

子が死亡した場合、シグナルのブロックを解除した後、SIGCHLD のシグナルハンドラーが呼び出されます。これはクリティカル セクションの概念です。あなたの場合、クリティカル セクションは の前に開始しfork()、後に終了しchildren.add()ます。

于 2008-12-04T12:20:24.597 に答える
0

クリティカル フラグメントを使用できない場合は、単純なカウンターでこの作業を行うことができます。追加の場合は +1、削除の場合は -1、どちらが先に発生しても、最終的にはすべてが完了した時点で 0 になります。

于 2010-06-09T01:29:10.577 に答える
-1

既存の「子供」に加えて、新しいデータ構造「早期死亡」を追加します。これにより、お子様の中身が清潔に保たれます。

  // main program excerpt
    for (;;) {
      if ( is_time_to_make_babies ) {

        pid = fork();
        if (pid == -1) {
          /* fail */
        } else if (pid == 0) {
          /* child stuff */
          print "child started"
          exit
        } else {
          /* parent stuff */
          print "parent forked new child ", pid
          if (!earlyDeaths.contains(pid)) {
              children.add(pid);
          } else {
              earlyDeaths.remove(pid);
          }
        }

      }
    }

  // SIGCHLD handler
  sigchld_handler(signo) {
    while ( (pid = wait(status, WNOHANG)) > 0 ) {
      print "parent caught SIGCHLD from ", pid
      if (children.contains(pid)) {
          children.remove(pid);
      } else {
          earlyDeaths.add(pid);
      }
    }
  }

編集:プロセスがシングルスレッドの場合、これは単純化できます-earlyDeathsはコンテナーである必要はなく、1つのpidを保持する必要があります。

于 2008-12-04T11:46:49.080 に答える
-1

多分楽観的アルゴリズム?children.remove(pid) を試してみてください。失敗した場合は、先に進みます。

または、削除する前に pid が子にあることを確認しますか?

于 2012-04-02T18:21:33.953 に答える