プロセス追跡とエラー処理、パート2

前文



この記事の最初の部分では 、プロセスとエラー伝播のプロセス間の関係のメカニズムを調べました。 今日は、前のパートで説明しなかった1つのケースを見てみましょう。killシグナルでプロセスを撮影します。



キルシグナル



キルシグナルは傍受されたシグナルではありません。 たとえシステムであっても、常にプロセスを強制終了します。 これは、失敗したプロセスを強制終了するためにOTPスーパーバイザーによって使用されます。 プロセスが強制終了シグナルを受信すると、プロセスは停止し、強制終了されたシグナルはすべてのリンクに送信されます。



すべての例で、このプロセスツリーが使用されます。





図1



小さなモジュールを書きましょう。

-module(test). -export([start/1, proc/2]). start(Sysproc) -> process_flag(trap_exit, Sysproc), io:format("Shell Pid: ~p~n", [self()]), PidA = spawn_link(test, proc, [self(), a]), PidB = spawn_link(test, proc, [self(), b]), io:format("ProcA: ~p~nProcB: ~p~n", [PidA, PidB]), exit(PidB, kill). proc(Shell, Tag) -> receive after 5000 -> Shell ! {hello_from, Tag, self()} end.
      
      







start / 1関数では、2つのプロセスを作成します:AとB、proc / 2関数はプロセスの本体です。その最初の引数はシェルのPid、2番目のTag:atom()は、メッセージがどのプロセスから来たのかを分析するのに便利です。 作成後、exit / 2関数が呼び出され、プロセスBにkillシグナルが送信されます。



コンパイルして実行します。



 (emacs@aleksio-mobile)2> test:start(false). Shell Pid: <0.36.0> ProcA: <0.43.0> ProcB: <0.44.0> ** exception exit: killed (emacs@aleksio-mobile)3> flush(). ok (emacs@aleksio-mobile)4> self(). <0.45.0> (emacs@aleksio-mobile)5>
      
      







start / 1関数を呼び出します(この例のシェルはシステムプロセスではないため、出力信号をキャッチできません)。 プロセスの生成後、「** exception exit:killed」というメッセージが表示されます。これは、シェルが原因で強制終了されたことを意味します。 シェルが本当に死んだということは、その新しいPid <0.45.0>によると勇敢な人の死です。 概略的に、プロセス全体は次のように表すことができます。





図2



エラーはすべてのリンクを介して伝播し、すべてのプロセスが停止します。 ここで、作成したプロセスをシステム化できるように、コードをわずかに変更します。



 -module(test). -export([start/1, proc/3]). start(Sysproc) -> process_flag(trap_exit, Sysproc), io:format("Shell Pid: ~p~n", [self()]), PidA = spawn_link(test, proc, [self(), a, false]), PidB = spawn_link(test, proc, [self(), b, true]), io:format("ProcA: ~p~nProcB: ~p~n", [PidA, PidB]), exit(PidB, kill). proc(Shell, Tag, Sysproc) -> process_flag(trap_exit, Sysproc), receive after 5000 -> Shell ! {hello_from, Tag, self()} end.
      
      







 (emacs@aleksio-mobile)2> test:start(false). Shell Pid: <0.36.0> ProcA: <0.43.0> ProcB: <0.44.0> ** exception exit: killed (emacs@aleksio-mobile)3> flush(). ok (emacs@aleksio-mobile)4> self(). <0.45.0> (emacs@aleksio-mobile)5>
      
      







プロセスBがシステム化されたという事実(図3の二重丸)にもかかわらず、キル信号を受信すると死に、その後、キルされた信号がリンクを介して伝播し、すべてのプロセスが死にます。





図3



では、シェルシステムも作成しましょう。



 (emacs@aleksio-mobile)2> test:start(true). Shell Pid: <0.36.0> ProcA: <0.43.0> ProcB: <0.44.0> true (emacs@aleksio-mobile)3> flush(). Shell got {'EXIT',<0.44.0>,killed} Shell got {hello_from,a,<0.43.0>} Shell got {'EXIT',<0.43.0>,normal} ok (emacs@aleksio-mobile)4> self(). <0.36.0> (emacs@aleksio-mobile)5>
      
      







プロセスBが強制終了され、シェルが終了シグナルをインターセプトできた(シェルが{'EXIT'、<0.44.0>、killed}を取得した)、プロセスAがメッセージを送信し、作業を正常に完了したことがわかります(図4)。





図4



別のケースをテストするために残っています。

 -module(test). -export([start/1, proc/4]). start(Sysproc) -> process_flag(trap_exit, Sysproc), io:format("Shell Pid: ~p~n", [self()]), PidA = spawn_link(test, proc, [self(), a, true, fun() -> ok end]), PidB = spawn_link(test, proc, [self(), b, false, fun() -> exit(kill) end]), io:format("ProcA: ~p~nProcB: ~p~n", [PidA, PidB]). proc(Shell, Tag, Sysproc, F) -> process_flag(trap_exit, Sysproc), F(), receive Msg -> io:format("Proc ~p get msg: ~p.~n", [Tag, Msg]) after 5000 -> Shell ! {hello_from, Tag, self()} end.
      
      







proc関数(受信ブロックの前に呼び出される関数)に追加の引数が追加されました。 プロセスAの場合、関数はokのみを返し、プロセスBの場合、killパラメーターを指定してexit / 1を呼び出します。 プロセス内で作成し、外部にシグナルを送信しません。 この実験のプロセスAは常に体系的です。 チェックを実行します。



 (emacs@aleksio-mobile)2> test:start(false). Shell Pid: <0.36.0> ProcA: <0.43.0> ProcB: <0.44.0> Proc a get msg: {'EXIT',<0.36.0>,killed}. ** exception exit: killed (emacs@aleksio-mobile)3> flush(). ok (emacs@aleksio-mobile)4> self(). <0.45.0> (emacs@aleksio-mobile)5>
      
      







関数start / 1(シェルを非システムにします)を呼び出しますが、予想どおり、シェルは落ちました。 プロセスBはkillシグナルによって殺され、その後シグナルがシェルに送信され、シェルが落下し、プロセスAによってシグナルがキャッチされました(図5)。





図5



注目の質問! プロセスBによってシェルに送信された信号(図には3つの疑問符があります)。 アイデアは殺されることです。 確認してみましょう-シェルシステムを作成します。



 (emacs@aleksio-mobile)2> test:start(true). Shell Pid: <0.36.0> ProcA: <0.43.0> ProcB: <0.44.0> ok (emacs@aleksio-mobile)3> flush(). Shell got {'EXIT',<0.44.0>,kill} Shell got {hello_from,a,<0.43.0>} Shell got {'EXIT',<0.43.0>,normal} ok (emacs@aleksio-mobile)4> self(). <0.36.0> (emacs@aleksio-mobile)5>
      
      







シェルはシグナル{'EXIT'、<0.44.0>、kill}を受け取りましたが、作業を完了しませんでした! 理由がすぐにわかりません。 たぶん、読者の一人は知っていますか?



次のパートでは、モニターのメカニズムについて検討します。



参照資料



1. 優れたオンラインドキュメント

2. Francesco CesariniとSimon ThompsonによるERLANGプログラミング。

3.プログラミングアーラン:ジョーアームストロングによる並行世界のためのソフトウェア。



All Articles