14

フォークする Perl スクリプトがあります。

各フォークは外部プログラムを実行し、出力を解析して、出力を保存可能なファイルに変換します。

次に、Storable ファイルが親によって読み込まれ、各子からの合計データが分析されてから、前のフォークの繰り返しに進むか、親が停止します。

一部の子がまだ外部プログラムを実行しているときに ^C を発行すると、正確にはどうなりますか? 親の perl スクリプトはフォアグラウンドで呼び出され、フォークしたにもかかわらずフォアグラウンドに残っていたと思います。

SIGINT はすべての子、つまり親、親の子、および子によって呼び出される外部プログラムに渡されますか??

更新

付け加えておくと、SIGINIT を発行すると、スクリプトの子によって呼び出された外部プログラムがシグナルを認識して終了するように見えます。しかし、子供たち、またはおそらく親プログラムは続けます。これは私にはすべて不明です。

更新 2 :

tchristさんのコメントですが、Perlのsystem()コマンドで外部プログラムを呼び出します。

実際、tchrist のコメントにも、私が探していた説明が含まれているようです。私のプログラムの動作に基づいてさらにデバッグした後、実際に、SIGINT が親からすべての子に、すべての子からすべての子 (外部プログラム) に渡されているように見えます。

したがって、tchrist のコメントに基づいて、起こっているように見えるのは、CTRL-C が外部プログラムを強制終了し、子プロセスがsystem()コマンドの外に出てしまうということです。

で呼び出されたものの終了ステータスを子供たちに確認させましたがsystem()、CTRL-C は、より多くの処理ラウンドの作成につながるのではなく、親から下のすべてを強制終了すると想定していました。 !

解決策(私の問題)

親で SIGINT のシグナルハンドラーを作成するだけです。次に、シグナルハンドラーは SIGTERM を各子に送信し (これにより、子の子にも SIGTERM が送信されると思います)、親を正常に終了させます。このやや明白な解決策で問題が修正される可能性はありますが、Perl での fork に関する SIGINT の動作に関する私の誤解を理解したいと思いました。

4

3 に答える 3

13

Perlの組み込み関数は、シグナルに関する限り、標準CライブラリのCシステムsystem(3)関数と同じように機能します。Perlのバージョンの、またはパイプオープンまたはバックティックを使用している場合、子が実行されている間、親(呼び出されたものではなく呼び出し側)がSIGINTおよびSIGQUITを無視します。あなたがトリオのいくつかの変種を使ってあなた自身を転がしたならば、あなたはこれらの問題についてあなた自身で考えなければなりません。system()systemfork?wait:exec

system("vi somefile")での長い検索中に^Cを使用してヒットするとどうなるかを考えてみてください。(致命的ではない)SIGINTviのみが必要です。vi親はそれを無視します。これは正しい動作です。これがCがこのように機能する理由であり、Perlがこのように機能する理由です。

覚えておく必要があるのは、^ Cがフォアグラウンドプロセスグループ内のすべてのプロセス(有効なUIDまたはGIDが異なるプロセスも含む)にSIGINTを送信するからといって、それらすべてのプロセスが終了するわけではないということです。^ Cは単なるSIGINTであり、プロセスを中断することを目的としており、SIGKILLではなく、質問をせずに終了することを目的としています。

警告なしにただ殺すのは間違っているであろう多くの種類のプログラムがあります。エディターはそのような例の1つにすぎません。メーラーは別かもしれません。これには非常に注意してください。

多くの種類のプログラムは、さまざまな種類の信号を選択的に無視、トラップ、またはブロック(配信の遅延を意味します)します。SIGINTのデフォルトの動作は、プロセスを終了させることだけです。従来のオペレーティングシステムでこの種のコードを使用すると、これが発生したかどうか、実際にどの信号が発生したかを確認できます。

if ($wait_status = system("whatever")) {
    $sig_killed   = $wait_status & 127;
    $did_coredump = $wait_status & 128;
    $exit_status  = $wait_status >>  8;
    # now do something based on that...
}

viたとえば、^ C'dには、トラップされていないSIGINTがなかったため、それがキャッチされたことを示す待機ステータスワードがないことに注意してください。

時々あなたの子供はあなたの後ろに彼ら自身の子供を連れて行きます。散らかっていますが本当です。したがって、私は時々、この方法で既知および未知のすべての子孫を虐殺することが知られています。

# scope to temporize (save+restore) any previous value of $SIG{HUP}
{
    local $SIG{HUP} = "IGNORE";
    kill HUP => -$$;   # the killpg(getpid(), SIGHUP) syscall
}

もちろん、これはSIGKILLやSIGSTOPでは機能しません。これらは、そのように無視されることはありません。

注意が必要なもう1つの問題は、5.8リリース以前は、Perlでの信号処理は歴史的に確実に安全な操作ではなかったことです。現在はです、これはバージョンに依存する問題です。まだ読んでいない場合は 、perlipcのマンページの遅延信号、およびおそらくperlrunのマンページの不変条件についても必ず読んPERL_SIGNALSください

于 2011-01-18T00:50:34.840 に答える
9

ターミナルウィンドウ(または任意のターミナル)で^ Cを押すと、そのターミナルのフォアグラウンドプロセスGROUPにSIGINTが送信されます。これで、コマンドラインからプログラムを起動すると、通常は独自のプロセスグループにあり、それがフォアグラウンドプロセスグループになります。デフォルトでは、子をフォークすると、その子は親と同じプロセスグループに含まれるため、デフォルトでは、親(コマンドラインから呼び出される最上位プログラム)、そのすべての子、子の子など、およびこれらのいずれかによって呼び出された外部プログラム(すべて子のみ)はすべて同じプロセスグループに含まれるため、すべてSIGINTシグナルを受信します。

ただし、これらの子またはプログラムのいずれかがsetpgrp、setpgid、setsid、またはプロセスを新しい進行状況グループに含めるその他の呼び出しを呼び出した場合、それらのプロセス(およびフォアグラウンドプロセスグループを離れた後に開始する子)は呼び出されません。 SIGINTを受け取ります。

さらに、プロセスがSIGINTを受信して​​も、終了しない場合があります。つまり、シグナルを無視している場合や、シグナルをキャッチしてまったく異なる処理を実行している場合があります。SIGINTのデフォルトのシグナルハンドラーはプロセスを終了しますが、これはオーバーライドできるデフォルトにすぎません。

編集

更新から、すべてが同じプロセスグループに残っているように見えるので、孫(外部プログラム)が終了するときにシグナルがすべての人に配信されますが、子供たちはSIGINTをキャッチして無視しています。tchristのコメントによると、これがperlのデフォルトの動作のようです。

于 2011-01-17T20:13:48.243 に答える
2

親プロセスを強制終了しても、子プロセス (および実行中の外部プログラム) は何らかの方法で終了するまで実行されます。これは、親に SIGINT をキャッチするシグナル ハンドラがあり、プロセス グループ (多くの場合、親の pid) を強制終了する場合に回避できます。そうすれば、親が SIGINT を取得すると、すべての子が殺されます。

あなたの質問に答えるために、それはすべてシグナルハンドラーの実装に依存します。あなたの更新に基づいて、親が実際に子を殺し、それ自体を終了する代わりに、何か他のことをするように見えます(完全なシャットダウン/開始の組み合わせではなく、リセットと考えてください)。

于 2011-01-17T19:48:09.690 に答える