Help us understand the problem. What is going on with this article?

リンクされたプロセスの終了時の挙動 #Erlang

More than 1 year has passed since last update.

概要

このページでは、リンクされたプロセスが終了した時にどのように処理されるかについてまとめている。

リンクされた プロセスが終了するときの挙動一覧

リンクされたプロセスが終了される時の挙動についての説明は 本:プログラミングErlang を見るとわかりやすい。
下の表はプログラミングErlangを参考に少し修正している。
(trap_exitで対比できるようにtrap_exitがtrueの時に終了シグナルnormalが飛んできた時を終了シグナルXの場合と分けて書いている。)

trap_exit 終了シグナル プロセスの生死 動作
true normal 続行する {'EXIT', Pid, normal} をメールボックスに追加する
true kill 死ぬ 終了シグナル killed をリンクセットにブロードキャストする
true X 続行する {'EXIT', Pid, X} をメールボックスに追加する
false normal 続行する 通常終了シグナルは無視される
false kill 死ぬ 終了シグナル killed をリンクセットにブロードキャストする
false X 死ぬ 終了シグナル X をリンクセットにブロードキャストする

exit(Why)exit(self(), Why) の違い

リンクしているプロセスが受け取るメッセージの終了理由が killkilled で異なる。

exit(kill) の場合

リンクしているプロセスは kill を受け取る

ex.exit(kill)
> process_flag(trap_exit, true).
false
> spawn_link(fun() -> exit(kill) end).
<0.35.0>
> flush().
Shell got {'EXIT',<0.35.0>,kill}
ok

exit(self(), kill) の場合

リンクしているプロセスは killed を受け取る

ex.exit(self(),kill)
> process_flag(trap_exit, true).
false
> spawn_link(fun() -> exit(self(), kill) end).
<0.35.0>
> flush().
Shell got {'EXIT',<0.35.0>,killed}
ok

関数の詳細なドキュメント

上の関数の挙動の違いは公式ドキュメントを読むと書かれている。
killkilled に変わるということが exit/2 にしか書かれていないことより。)
また、プログラミングErlangのドキュメントも載せておく。

exit/1

exit/1
-spec exit(Reason) -> no_return() when
      Reason :: term().

公式ドキュメント(和訳)

終了理由 Reason で呼び出しプロセスの実行を停止させる。 Reason はどんな項でも良い。この関数の評価はプロセスを終了させるため、戻り値はない。

プログラミングErlang

現在のプロセスを要因 Reason で終了させる。この文を実行している節が catch 文のスコープの中にない場合は、現在リンクされているすべてのプロセスに対して引数 Reason を伴う終了シグナルをブロードキャストする。
(プログラミングErlangより。 WhyReason に置き換えている。)

exit/2

exit/2
-spec exit(Pid, Reason) -> true when
      Pid :: pid() | port(),
      Reason :: term().

公式ドキュメント(和訳)

Pid で識別されるプロセスまたはポートに終了理由 Reason で終了信号を送信する。

Reasonnormalkill 以外の任意の項である場合は、次の動作が適用される。

Pid が終了を捕捉していない場合は、 Pid 自体が終了理由 Reason で終了する。 Pid が終了を捕捉している場合は、終了シグナルがメッセージ {'EXIT', From, Reason} に変換され、 Pid のメッセージキューに追加される。 From は終了シグナルを送ったプロセスのpidである。 process_flag/2 も参照。

Reason がアトム normal の場合、 Pid は終了しない。終了を補足している場合、終了シグナルはメッセージ {'EXIT', From, normal} に変換されメッセージキューに追加される。

Reason がアトム kill の場合、つまり exit(Pid, kill) が呼ばれた場合、補足されない終了シグナルが送られ、終了理由 killed で無条件に終了する。

プログラミングErlang

要因が Why の終了シグナルをプロセス Pid に送信する。

gen_serverを停止させるときの挙動一覧

リンクされたgen_serverについて、 exit 時の状態、 exit の送り主によって「何も呼ばれずに死ん」だり、「 terminate/2 が呼ばれ」たり、「 handle_info/2 で補足し」たりするので、ここでまとめておく。

ここで Srv はgen_serverのプロセスID( pid() )である。

trap_exit 終了処理 送り主 動作 リンクプロセスへの終了シグナル ERROR REPORTの出力
false exit(Srv, normal) 親プロセス 死なない - なし
false exit(Srv, kill) 親プロセス 死ぬ killed なし
false exit(Srv, X) 親プロセス 死ぬ X なし
false Srv ! {'EXIT', self(), normal} 親プロセス terminate/2 の呼び出し normal なし
false Srv ! {'EXIT', self(), kill} 親プロセス terminate/2 の呼び出し kill あり
false Srv ! {'EXIT', self(), X} 親プロセス terminate/2 の呼び出し X あり
true exit(Srv, normal) 親プロセス terminate/2 の呼び出し normal なし
true exit(Srv, kill) 親プロセス 死ぬ killed なし
true exit(Srv, X) 親プロセス terminate/2 の呼び出し X あり
true Srv ! {'EXIT', self(), normal} 親プロセス terminate/2 の呼び出し normal なし
true Srv ! {'EXIT', self(), kill} 親プロセス terminate/2 の呼び出し kill あり
true Srv ! {'EXIT', self(), X} 親プロセス terminate/2 の呼び出し X あり
false exit(Srv, normal) 親以外のプロセス 死なない - なし
false exit(Srv, kill) 親以外のプロセス 死ぬ killed なし
false exit(Srv, X) 親以外のプロセス 死ぬ X なし
false Srv ! {'EXIT', self(), normal} 親以外のプロセス handle_info/2 の呼び出し - なし
false Srv ! {'EXIT', self(), kill} 親以外のプロセス handle_info/2 の呼び出し - なし
false Srv ! {'EXIT', self(), X} 親以外のプロセス handle_info/2 の呼び出し - なし
true exit(Srv, normal) 親以外のプロセス handle_info/2 の呼び出し - なし
true exit(Srv, kill) 親以外のプロセス 死ぬ killed なし
true exit(Srv, X) 親以外のプロセス handle_info/2 の呼び出し - なし
true Srv ! {'EXIT', self(), normal} 親以外のプロセス handle_info/2 の呼び出し - なし
true Srv ! {'EXIT', self(), kill} 親以外のプロセス handle_info/2 の呼び出し - なし
true Srv ! {'EXIT', self(), X} 親以外のプロセス handle_info/2 の呼び出し - なし

gen_server:stop/1,3

gen_server:stop/1,3 はErlang/OTP 18で追加された関数である。
この関数も停止処理に使うものということでここに公式ドキュメントを和訳しておく。
公式ドキュメントは以下のURLにある。
http://www.erlang.org/doc/man/gen_server.html#stop-1
http://www.erlang.org/doc/man/gen_server.html#stop-3

spec

stop/1,3
-spec stop(ServerRef) -> ok when
      ServerRef :: Name | {Name, Node} | {global, GlobalName} | {via, Mdule, ViaName} | pid(),
      Node :: atom(),
      GlobalName :: term(),
      ViaName :: term().

-spec stop(ServerRef, Reason, Timeout) -> ok when
      ServerRef :: Name | {Name, Node} | {global, GlobalName} | {via, Mdule, ViaName} | pid(),
      Node :: atom(),
      GlobalName :: term(),
      ViaName :: term(),
      Reason :: term(),
      Timeout :: pos_integer() | infinity.

関数の説明(和訳)

与えられた Reason で汎用サーバに終了させる命令を送り、終了を待つ。gen_serverは終了する前に Module:terminate/2 を呼ぶ。

サーバが期待される理由で終了した場合、関数は ok を返す。 normalshutdown{shutdown, Term} 以外の理由の場合、 error_logger:format/2 を使ってerror reportが発生する。デフォルトの Reasonnormal である。

Timeout は0以上の整数であり、サーバが終了するのを待つまでのミリ秒単位で指定する。または、アトム infinity を指定すると永遠に待ち続ける。デフォルトの値は infinity である。サーバが指定した時間で終わらなかった場合は、 timeout 例外が発生する。

プロセスが存在しない場合は、 noproc 例外が発生する。

link/1 の挙動( PidOrPort が存在しない場合の挙動)

リンク時の状態によって、リンク先のプロセス PidOrPort がない場合の挙動が異なるので、ここに公式ドキュメントを和訳しておく。

公式ドキュメント(和訳)

既に呼び出しプロセスと別のプロセス(またはポート) PidOrPort との間にリンクがなければ、そのリンクを作る。プロセスは自身へのリンクを作成しようとした時は何もしない。 true を返す。

PidOrPort が存在しない場合、BIFの振る舞いは 呼び出しプロセスが終了を補足しているか どうかによる( process_flag/2 を参照):

  • 呼び出しプロセスが終了を補足していない、かつ、 PidOrPort のチェックがチープな場合(つまり、 PidOrPort がローカルにある場合)link/1 は理由 noproc で失敗 する。
  • そうでない場合は、呼び出しプロセスが終了を補足している場合、かつ/または、 PidOrPort がリモートにある場合link/1true を返すが、理由 noproc を伴う終了シグナルが呼び出しプロセスに送られる。
KOU_CHANG
2013年4月より、仕事でErlangを書き続け、Erlangにどっぷりハマったエンジニアです。 元々、「好きなプログラミング言語は?」と聞かれても特になかったのですが、Erlangを書いている時はテンションが高いですw 特に、Erlangの日本語ドキュメントが少ないことと、自分自身のもうワンステップ実力の向上の一環として、「ドキュメントを書こう!」と思いQiitaに記事を上げ始めました。
dwango
Born in the net, Connected by the net.
https://dwango.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした