16
Help us understand the problem. What are the problem?

More than 5 years have passed since last update.

Organization

`exit` の挙動 #Erlang

概要

このページでは、プロセスが exit される時にどのように処理されるかについてまとめている。

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

リンクされたプロセスが終了される時の挙動についての説明は 本:プログラミング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 notmal 続行する 通常終了シグナルは無視される
false kill 死ぬ 終了シグナル killed をリンクセットにブロードキャストする
false X 死ぬ 終了シグナル X をリンクセットにブロードキャストする

kill の使い方

終了理由 kill は上の表にもある通り、 process_flag(trap_exit, true) で補足できないことにある。
例えば、無限ループに入っていたり、 receive で終了メッセージにマッチングしないメッセージのみで待ち受けていたりするときである。

具体的な例を次に示す。

> process_flag(trap_exit, true). % 終了理由を取得するために exit をトラップする
false
> Loop = fun Loop () -> timer:sleep(5000), Loop() end. % ループする関数
#Fun<erl_eval.44.54118792>
%% 終了シグナルをトラップするようにしたうえで無限ループをするプロセスを生成する
> Pid = spawn_link(fun () -> process_flag(trap_exit, true), Loop() end).
<0.46.0>
> exit(Pid, reason). % 終了理由 reason で終了させようとする
true
> erlang:is_process_alive(Pid).
true % 終了しない
> flush().
ok % 終了理由も送られていない
> exit(Pid, kill). % 終了理由 kill で終了させようとする
true
> erlang:is_process_alive(Pid).
false % 終了している
> flush().
Shell got {'EXIT',<0.46.0>,killed} % 終了理由は killed で終わっていることがわかる
ok

もちろん、終了を補足していない時は終了理由 reason でも終了する。

> process_flag(trap_exit, true). % 終了理由を取得するために exit をトラップする
false
> Loop = fun Loop () -> timer:sleep(5000), Loop() end. % ループする関数
#Fun<erl_eval.44.54118792>
%% 無限ループをするプロセスを生成する(終了シグナルはトラップしない)
> Pid = spawn_link(fun () -> Loop() end).
<0.46.0>
> exit(Pid, reason). % 終了理由 reason で終了させようとする
true
> erlang:is_process_alive(Pid).
false % 終了している
> flush().
Shell got {'EXIT',<0.46.0>,reason} % 終了理由は reason で終わっていることがわかる
ok

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 に送信する。

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
16
Help us understand the problem. What are the problem?