概要
このページでは、プロセスが 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) の違い
リンクしているプロセスが受け取るメッセージの終了理由が kill と killed で異なる。
exit(kill) の場合
リンクしているプロセスは 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 を受け取る
> 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
関数の詳細なドキュメント
上の関数の挙動の違いは公式ドキュメントを読むと書かれている。
( kill が killed に変わるということが exit/2 にしか書かれていないことより。)
また、プログラミングErlangのドキュメントも載せておく。
exit/1
-spec exit(Reason) -> no_return() when
Reason :: term().
公式ドキュメント(和訳)
終了理由 Reason で呼び出しプロセスの実行を停止させる。 Reason はどんな項でも良い。この関数の評価はプロセスを終了させるため、戻り値はない。
プログラミングErlang
現在のプロセスを要因 Reason で終了させる。この文を実行している節が catch 文のスコープの中にない場合は、現在リンクされているすべてのプロセスに対して引数 Reason を伴う終了シグナルをブロードキャストする。
(プログラミングErlangより。 Why を Reason に置き換えている。)
exit/2
-spec exit(Pid, Reason) -> true when
Pid :: pid() | port(),
Reason :: term().
公式ドキュメント(和訳)
Pid で識別されるプロセスまたはポートに終了理由 Reason で終了信号を送信する。
Reason が normal か kill 以外の任意の項である場合は、次の動作が適用される。
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 に送信する。