質問

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

上記の例では、競合状態があります。 <!> quot; /* child stuff */ <!> quot;は可能です。 <!> quot; /* parent stuff */ <!> quot;の前に終了します。終了すると、子のpidが終了後に子のリストに追加され、削除されることはありません。アプリが終了するときが来ると、親は既に終了した子が終了するまで延々と待ちます。

これに対抗するために考えられる1つの解決策は、2つのリストstarted_childrenfinished_childrenを持つことです。 childrenに追加するのと同じ場所で.addに追加します。しかし、シグナルハンドラでは、.removeから削除する代わりに、sleep(1) add します。アプリが終了すると、親は単に<=>と<=>の差がゼロになるまで待つことができます。

考えられるもう1つの解決策は、共有メモリを使用することです。親の子のリストを共有し、子<=>および<=>を自分自身にさせますか?しかし、私はこれについてあまり知りません。

編集:頭に浮かんだ最初の問題であった別の可能な解決策は、単に<=>の先頭に<=>を追加することですが、それは私にとって面白くないにおいです。また、100%修正されているかどうかもわかりません。

では、この競合状態をどのように修正しますか?また、このための確立された推奨パターンがある場合は、お知らせください!

ありがとう。

役に立ちましたか?

解決

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

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

他のヒント

重要なフラグメントを使用できない場合、単純なカウンターでこの作業を実行できます。追加時に+1、削除時に-1、最初に発生する問題はありません。すべてが完了すると、最終的にゼロになります。

既存の<!> quot; children <!> quotに加えて、新しいデータ構造<!> quot; early deaths <!> quot;を追加します。これにより、子供のコンテンツがきれいに保たれます。

  // 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を保持するだけです。

おそらく楽観的なアルゴリズムでしょうか? children.remove(pid)を試してみて、失敗した場合は先に進みます。

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

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top