0プリアンブル
家庭内のすべてが制御下にあり、すべてが整頓されている場合、それぞれがその場所に立って、その普遍的な目的を明確に果たします。 今日は、非常に多様なアーランプロセスでの秩序の編成を検討します。 Erlangプロセスに関する基本的な概念は、この投稿で見つけることができます。
1あなたは私に従う-私はあなたに従う
何らかの方法でアーランをよく知っていた人は誰でも、「プロセスを落として、もう一方がそれについて何かをするか、問題に対処する」というフレーズを聞きました。 同意します。何かが壊れた場合、それは悪いことですが、長い間それを知らなければ、それは二重に悪いことです。 あなたの猫はミルクのボウルを壊し、あなたからこの恐ろしい事実を隠しました-悪い! ボウルが猫を追いかけ、猫がボウルを追いかけます。 このような粗雑な比較を著者の読者に許してください。 それでは、ビジネスに取り掛かりましょう。
相互の状態を監視するためのプロセス間の接続は、アーランの基本概念の1つです。 複雑で適切に設計されたシステムでは、「空中に浮かぶ」プロセスはありません。 すべてのプロセスはコントロールツリーに埋め込まれ、そのリーフはワークプロセスであり、内部ノードはワーカー(コントローラー)を監視します[2 |参照 OTP(Open Telecom Platform)の原則]。 2人のワーカーを接続することは可能ですが。
![](https://habrastorage.org/storage/d2ef30c7/ee39aad4/f2d9ec57/e79dbbe5.png)
図1
OTPが提供する抽象化のレベルに達しない場合、Erlangにはプロセスを接続するための2つのメカニズムがあります。
- リンク-2つのプロセス間の双方向リンク。
- モニターは、オブザーバープロセスとオブザーバブル間の一方向の接続です。
1.1関係
次の関数は、プロセス間の関係を作成するために使用されます。
- erlang:link / 1-呼び出し元の関数と別のプロセス間の接続を作成します。
- erlang:spawn_link / 1/2/3/4(別名proc_lib:spawn_link / 1/2/3/4)-新しいプロセスを作成し、それを関数を呼び出すプロセスにリンクします。
- erlang:unlink / 1-関数を呼び出すプロセスと引数で指定されたプロセスの間の接続を削除します。
- pool:pspawn_link / 3-プール内のノードの1つで新しいプロセスを作成し、それを関数を呼び出すプロセスにリンクします。
それは私たちに、プロセス間の双方向通信を提供しますか? 関係は、エラーの伝播方法を決定します。 1つのプロセスが停止し、2番目のプロセスがこのことを発見し、以下で検討するいくつかのケースで、そのプロセスも完了し、それに接続されている他のすべてのプロセスにシグナルを送信します。 このメカニズムにより、リモートでエラーを処理できます。 ハンドラーは個別のプロセス(コントローラー)にすることができ、これらすべてのエラーはリンクを介して「フロー」し、ハンドラープロセスはすべて別のノードに配置できます。 そして、これらのすべての利点はほとんど無料です-すべてがプラットフォームにすでに実装されているので、メガスーパーフェール分散システムを正しく構築する必要があります。
![](https://habrastorage.org/storage/c27f1e22/d6df04c6/495d18a2/09677f0a.png)
図2
プロセスがクラッシュすると(図2を参照)、リンクされたすべてのプロセスに出力シグナルが送信されます。このシグナルには、どのプロセスとどのような理由で戦闘で死亡したかに関する情報が含まれます。 シグナルはタプル{'EXIT'、Pid、Reason}です。
Reason変数には2つの事前定義値があります。
- normal-理由のこの値は、プロセスがロードしたすべての作業を完了した場合に設定されます。 呼び出された関数の最後に到達しただけです。 この場合、それにリンクされているプロセスは作業を完了しません。
- kill-システムを含め、常にプロセスを強制終了するインターセプトされないシグナルは、失敗したプロセスの強制終了に使用されます。
プロセスが出力信号をインターセプトできるようにするには、process_flag関数(trap_exit、true)を呼び出してtrap_exitフラグを設定することにより、システムを作成する必要があります。
それで、十分な理論、実際にすべてを試してみましょう。 お気に入りのエディターを開き、小さなモジュールを作成します。 まず、プロセスの正常終了をテストしましょう。 実験を簡単にするために、プロセスの1つとしてシェルを用意します。
-module(links_test). -export([start_n/1, loop_n/1]). start_n(Sysproc) -> %% test normal reason process_flag(trap_exit, Sysproc), io:format("Shell Pid: ~p~n", [self()]), Pid = spawn_link(links_test, loop_n, [self()]), io:format("Process started with Pid: ~p~n", [Pid]). loop_n(Shell) -> %% loop for test normal reason receive after 5000 -> Shell ! timeout end.
二つの機能によって定義されたモジュールで:パラメータは、プロセスシステムを作るブールの値をとるように、(この場合、このシェルであろう)第START_N新しいプロセスを作成し、呼び出しプロセスにリンク。 2番目のloop_nは、作成されるプロセスの本体です。引数として、呼び出しプロセス(シェル)のPidを渡します。 プロセスが開始してから5秒後に、タイムアウトメッセージをシェルに送信します。 プロセスをコンパイルして実行します。
(emacs@aleksio-mobile)2> links_test:start_n(false). Shell Pid: <0.36.0> Process started with Pid: <0.43.0> ok (emacs@aleksio-mobile)3> flush(). Shell got timeout ok (emacs@aleksio-mobile)4>
falseパラメーターを指定してlinks_test:start_n関数を呼び出します。 シェルではなく、出力信号をキャッチすることはできませんプロセス。 私たちは通り、プロセスが正常に作成されていることがわかり loop_n関数に末尾再帰はありません。正常に実行され、プロセスは終了します。 flush()関数を呼び出してシェルメールボックスからすべてのメッセージを破棄すると、プロセス「Shell got timeout」からメッセージが受信されたことがわかります。 このタイプの信号を処理するためのフラグが設定されていないため、出力信号は表示されません。 シェルをシステムプロセスにします。
(emacs@aleksio-mobile)5> links_test:start_n(true). Shell Pid: <0.36.0> Process started with Pid: <0.51.0> ok (emacs@aleksio-mobile)6> flush(). Shell got timeout Shell got {'EXIT',<0.51.0>,normal} ok (emacs@aleksio-mobile)8>
関数を実行すると、タイムアウトメッセージに加えて、プロセス{'EXIT'、<0.51.0>、normal}からの正常終了に関するメッセージが受信されたことがわかります。 すばらしいメカニズムにより、プロセスが作業を完了したことを確認する必要がある場合にコードの量を節約できます(「私はすべてをやった」というシグナルを送信する必要はありません)。
今度は、通常と異なるエラーを生成してみましょう。 下記のリストにモジュールコードを変更します。
-module(links_test). -export([start_n/1, loop_n/1]). start_n(Sysproc) -> %% test abnormal reason process_flag(trap_exit, Sysproc), io:format("Shell Pid: ~p~n", [self()]), Pid = spawn_link(links_test, loop_n, [self()]), io:format("Process started with Pid: ~p~n", [Pid]). loop_n(Shell) -> %% loop for test abnormal reason receive after 5000 -> Shell ! timeout, 1 / 0 end.
私たちは非常に厳しいので、ゼロで除算することにしました。コンパイラーは私たちが間違っていることを自然に警告しますが、彼の警告を無視します。
(emacs@aleksio-mobile)33> links_test:start_n(false). Shell Pid: <0.117.0> Process started with Pid: <0.120.0> ok (emacs@aleksio-mobile)34> ** exception error: bad argument in an arithmetic expression in function links_test:loop_n/1 (emacs@aleksio-mobile)34> =ERROR REPORT==== 25-Feb-2011::16:22:48 === Error in process <0.120.0> on node 'emacs@aleksio-mobile' with exit value: {badarith,[{links_test,loop_n,1}]} (emacs@aleksio-mobile)34> flush(). ok (emacs@aleksio-mobile)35> self(). <0.122.0> (emacs@aleksio-mobile)36>
終了値を持つ'aleksioモバイル@ Emacsの:{badarith、[{links_test、loop_n、 (emacs@aleksio-mobile)33> links_test:start_n(false). Shell Pid: <0.117.0> Process started with Pid: <0.120.0> ok (emacs@aleksio-mobile)34> ** exception error: bad argument in an arithmetic expression in function links_test:loop_n/1 (emacs@aleksio-mobile)34> =ERROR REPORT==== 25-Feb-2011::16:22:48 === Error in process <0.120.0> on node 'emacs@aleksio-mobile' with exit value: {badarith,[{links_test,loop_n,1}]} (emacs@aleksio-mobile)34> flush(). ok (emacs@aleksio-mobile)35> self(). <0.122.0> (emacs@aleksio-mobile)36>
シェルPID = <0.117.0>に注意してください。 5秒後にエラーが発生し、結局私たちが間違っていたことを説明します。 シェルのキューにあるものを見てみましょう、そこに空です。 タイムアウトレターはどこにありますか? self()コマンドを実行してみましょう。ShellPidは<0.122.0>に等しくなりました。これは、失敗したプロセスが原因{badarith、[{links_test、loop_n、1}]}でシェル終了シグナルを送信したことを意味します。システムプロセスではなく、安全にクラッシュし、何らかの種類のコントローラーによって再起動されました(おそらく今後の記事で説明します)。 次に、出力信号処理フラグを有効にします。
(emacs@aleksio-mobile)40> links_test:start_n(true). Shell Pid: <0.132.0> Process started with Pid: <0.139.0> ok (emacs@aleksio-mobile)41> =ERROR REPORT==== 25-Feb-2011::16:34:19 === Error in process <0.139.0> on node 'emacs@aleksio-mobile' with exit value: {badarith,[{links_test,loop_n,1}]} (emacs@aleksio-mobile)41> flush(). Shell got timeout Shell got {'EXIT',<0.139.0>,{badarith,[{links_test,loop_n,1}]}} ok (emacs@aleksio-mobile)42> self(). <0.132.0> (emacs@aleksio-mobile)43>
終了値を持つ'aleksioモバイル@ Emacsの:{badarith、[{links_test、loop_n、 (emacs@aleksio-mobile)40> links_test:start_n(true). Shell Pid: <0.132.0> Process started with Pid: <0.139.0> ok (emacs@aleksio-mobile)41> =ERROR REPORT==== 25-Feb-2011::16:34:19 === Error in process <0.139.0> on node 'emacs@aleksio-mobile' with exit value: {badarith,[{links_test,loop_n,1}]} (emacs@aleksio-mobile)41> flush(). Shell got timeout Shell got {'EXIT',<0.139.0>,{badarith,[{links_test,loop_n,1}]}} ok (emacs@aleksio-mobile)42> self(). <0.132.0> (emacs@aleksio-mobile)43>
私はすべてがクリアされ、コメントが結果に不要だと思います。
4つのケースを調査しました。
シグナルtrap_exit | プロセスを完了する理由 | 残ったプロセス・アクション、「生きています」 |
---|---|---|
本当 | 普通の | メッセージ{'EXIT'、Pid、normal}がメールボックスに到着します |
偽 | 普通の | プロセスは作業を続行します |
本当 | 通常および殺害以外 | メッセージ{'EXIT'、Pid、Reason}がメールボックスに到着します |
偽 | 通常および殺害以外 | プロセスは終了し、すべての接続に終了信号を送信します(つまり、エラーの伝播が発生します) |
おわりに
次の記事( パート2 、 パート3 )では、モニターのメカニズムを検討し、killシグナルを使用してプロセスを撃ちます。 habrozhitelの意見を聞きたいのですが、アーランのどのトピックが最も興味深いでしょうか?
参照資料
1. 優れたオンラインドキュメント 。
2. OTPの原則 。
3. Francesco CesariniとSimon ThompsonによるERLANGプログラミング。
4.プログラミングアーラン:ジョーアームストロングによる並行世界のためのソフトウェア。