Erlangとそのプロセス

0プリアンブル



モデルは世界ではありません。 人間として、現実を完全に理解することはできません。 私たちはそのモデルを構築し、それを通して実世界を研究し、使用することができます。 情報空間(または私たちの頭)の一部の現実の完全性、成功、生存可能性は、選択するモデルによって異なります。



各プログラミング言語には、現実を構築するための独自のパラダイムがあります。 関数型言語では、計算プロセスは関数の値を計算するものとして解釈され、命令型言語では逆に、計算プロセスはプログラムの状態を変更する命令の形式で記述されます。



この記事では、著者は関数型プログラミング言語であるErlangに焦点を当てます。 この記事の最初の部分では、プロセス間のプロセスの作成と通信に関する背景情報を提供し、2番目では、Erlang仮想マシン内のプロセスの計画とプロセス仕様に焦点を当てます。 この記事は、Erlang言語で複雑でマルチスレッドでフォールトトレラントなアプリケーションの作成を開始したい初心者を対象としています。



1プロセスを操作する



Erlang言語は、仮想マシン(BEAM(Bogdan /BjörnのErlang Abstract Machine))で実行されるプロセスの軽量モデルを実装します。このモデルにより、次のことが可能になります。



1.1プロセス作成


次の関数は、プロセスを作成するために使用されます(スラッシュは、関数が取ることができる引数の数を示します)。



これらの機能に加えて、ドキュメンテーション[1]には、プロセスのサービスと操作を目的とした多くの機能があります。

それでは、簡単なプロセスを作成して実行しましょう。 対話型シェルを起動して、次のコマンドを実行します。



Eshell V5.8 (abort with ^G)

1> Fun = fun() -> receive after infinity -> ok end end.

#Fun<erl_eval.20.67289768>

2> Pid = spawn(Fun).

<0.33.0>

3>








最初の行では、プロセスの本体となる関数を作成し、spawn / 1関数を使用してプロセス自体を作成します。プロセスが作成され、識別子<0.33.0>が割り当てられます。 次に、関数c:i / 0を呼び出します(cは、Erlangシェルでのインタラクティブな作業のための関数を含むモジュールです)。



3> i().

Pid Initial Call Heap Reds Msgs

Registered Current Function Stack

<0.0.0> otp_ring0:start/2 987 2581 0

...

<0.33.0> erlang:apply/2 233 18 0

erl_eval:receive_clauses/8 10








プロセスが機能していることがわかります。 また、プロセスを監視するために、pmanグラフィカルユーティリティを実行できます。



5> pman:start().

<0.37.0>

6>












1.2プロセス間の通信


次に、プロセスが相互に通信する方法の最も単純な例を見てみましょう。 これを行うために、小さなモジュールを作成します(Erlangでプログラムを作成およびデバッグするために、著者はEmacs + Erlangモード+ distelの束を使用します):

  1. -module(proc)。
  2. -export([start / 0、p / 0])。
  3. 開始()->
  4. spawn(proc、p、[])。
  5. p()->
  6. 受け取る
  7. {Pid、Msg} when is_pid(Pid)->
  8. io:format( "Hello from proc:〜p、mesg:〜p〜n" 、[Pid、Msg])、
  9. うん! メッセージ
  10. p();
  11. 停止->
  12. わかった。
  13. _->
  14. io:形式( 「不明なタイプのメッセージ〜n」 、[])、
  15. p()
  16. 終わり。


モジュールの構造は非常に単純です:最初の行はモジュールの名前を記述し、ファイルの名前と一致する必要があります(この場合、ファイルはproc.erlと呼ばれます)、2行目では2つの関数をエクスポートします。1行目はプロセスを作成し、2行目は身体機能。 プロセスを作成するために、3つの引数を持つspawn関数のバージョンを使用します。これらは、タプル{M、F、A}で記述できます。ここで、Mは、関数Fが存在するモジュールで、引数Aのリストで呼び出されます(この場合、関数には引数がありません) 。 プロセス本体には次の構造が使用されます。

Receive

Pattern1 when Guard1 -> exp-11,...exp-1n;

...

Pattern1 when Guard1 -> exp-m1,...exp-mn

after Time -> exp-k1,...exp-kh

End








ここで、Patternは受信したメッセージを照合するためのテンプレートです。Guardの追加条件、Time-タイマー(ミリ秒単位)は、指定した時間キューが空の場合にトリガーされます。



ここでモジュールをコンパイルし、プロセスを作成してみてください。



1> c("proc").

{ok,proc}

2> Pid = proc:start().

<0.37.0>

3> Pid ! {self(), "Hello from shell"}.

Hello from proc: <0.30.0>, mesg: "Hello from shell"

{<0.30.0>,"Hello from shell"}

4> flush().

Shell got "Hello from shell"

ok

5>








最初の行では、モジュールをコンパイルしてからプロセスを作成します。 変数Pidは、演算子を使用して、タプル{self()、 "..."}の形式でメッセージを送信するプロセス識別子を格納します。ここで、self / 0関数は、現在のプロセスの識別子を返します-プロセスは対話型シェルです。



メッセージを作成すると、作成されたプロセスはそれをキューから取り出し、この場合は{Pid、Msg}とテンプレートの1つと比較し、その後、受信したメッセージを送り返します。 この場合のflush / 0関数は、対話型シェルに送信されたすべてのメッセージを破棄するために必要です。これは、このプロセスには受信ブロックがないためです。



慣例として、プロセスを交換する2つの独立したプロセスを作成することを読者に提案します。たとえば、Eralangシェルからのコマンドで、最初のプロセスは2番目に番号のペアを送信し、それらを合計して最初のプロセスに返送します。



2深く掘る





プロセスに関連するいくつかの問題を見てみましょう。まず、プロセススケジューラがどのように機能するか、次にプロセスについて学習できる情報を確認します。



2.1プロセススケジューラ




Erlangプロセスの計画は、削減に基づいています。 論理と数学の削減は、複雑なものを単純なものに減らす論理的かつ方法論的な方法です(Wikipedia)。 1つの削減は、関数呼び出しとほぼ同等です。 プロセスは、入力を待って中断されるまで(別のプロセスからのメッセージ、この場合中断されたプロセスが受信ブロックにある)、またはN個の削減が使い果たされるまで(正確な数は不明ですが、どこかで1000であることが判明するまで)実行できます削減)。



計画プロセスに影響を与えることができる関数(erlang:yield / 0、erlang:bump_reductions / 1)がありますが、まれな場合にのみ使用する必要があります(ドキュメント[1]に記載されているように、これらの関数は将来のリリースで変更/削除できます) ) メッセージを待機するプロセスは、メッセージがキューに表示されるか、受信ブロックのタイマーが起動するとすぐに再スケジュールされ、その後スケジューラキューの最後に配置されます。



Erlangには、最大(max)、高(high)、通常(normal)、低(low)の異なる優先順位を持つ4つのラインがあります。 スケジューラは、優先度maxでキュー内のプロセスを最初に検索し、キューが空になるまでそれらを開始します。次に、高キュー内のプロセスについても同様です。 次に、最大プロセスと高プロセスがなくなると、スケジューラは通常の優先度でプロセスを開始し、キューが空になるまで、またはプロセスが特定の数の削減を実行するまで、スケジューラは優先度が低いプロセスを処理します。



たとえば、通常の優先度と低の優先度は入れ替えることができます。たとえば、通常の優先度を持つプロセスが何百もあり、低の優先度を持つプロセスがいくつかあります。この場合、スケジューラは優先度が低いプロセスを最初に実行し、その後にのみ通常のプロセスを実行できます。



2.2内部プロセス情報




プロセスを操作するための多くの関数のうち、非常に興味深い関数が1つあります。erlang:process_info / 1またはerlang:process_info / 2この関数を使用すると、プロセスディクショナリ、ガベージコレクタ、ヒープサイズ、リンクされたプロセスなどの興味深い情報(詳細ドキュメントのリストを参照してください[1])。



関数の最初のバリアント(引数が1つ)は、デバッグの目的でのみ使用することをお勧めします。プロセスの完全な仕様を提供します。



次のプロセス情報を出力する簡単な関数を書きましょう。 プロセスが生成された機能。 リンクされたプロセスのリスト。



  1. -モジュール(テスト)。
  2. -export([情報/ 1])。
  3. info(Pid)->
  4. 仕様= [登録名、初期呼び出し、リンク]、
  5. ケース process_info(Pid、Spec)の
  6. 未定義->
  7. 未定義
  8. 結果->
  9. [{pid、Pid} |結果]
  10. 終わり。




5行目では、プロセスに関する情報を受け取るための仕様を作成します。 シェルを起動し、モジュールをコンパイルしてテストします。



3> processes().

[<0.0.0>,<0.3.0>,<0.5.0>,<0.6.0>,<0.8.0>,<0.9.0>,<0.10.0>,

<0.11.0>,<0.12.0>,<0.13.0>,<0.14.0>,<0.15.0>,<0.16.0>,

<0.17.0>,<0.18.0>,<0.19.0>,<0.20.0>,<0.21.0>,<0.23.0>,

<0.24.0>,<0.25.0>,<0.26.0>,<0.30.0>]

4> test:info(pid(0,0,0)).

[{pid,<0.0.0>},

{registered_name,init},

{initial_call,{otp_ring0,start,2}},

{links,[<0.5.0>,<0.6.0>,<0.3.0>]}]

5>








3行目では、実行中のすべてのプロセスのリストを取得し、関数を呼び出します。これは、登録されたプロセス名<0.0.0>がinitであることを示します。これは、otp_ring0:start / 2を呼び出すことで生成され、他の3つのプロセスがリンクされています。



次の記事では、プロセスを相互にリンクし、それらのステータスを追跡する方法について説明します。



参照資料



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

2. プロセスに関する基本情報

3. 仮想マシン設計の最前線で

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

5. プロセス計画について



All Articles