質問
次の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_children
とfinished_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が子にあることを確認しますか?