erlang init stop浅析
原创文章,转载请注明: 转载自庆亮的博客-webgame架构
本文链接地址: erlang init stop浅析
作者:庆亮 (qing.liang.cn@gmail.com)
日期:2010-08-03
boot之后关注的自然是stop,直接打开erts-5.7.5/src/init.erl源码看了看:
-spec stop() -> no_return().
stop() -> init ! {stop,stop}, ok.
{stop,Reason} ->
stop(Reason,State);
最终的调用:
stop(Reason,State) ->
BootPid = State#state.bootpid,
{_,Progress} = State#state.status,
State1 = State#state{status = {stopping, Progress}},
clear_system(BootPid,State1),
do_stop(Reason,State1).
看看 clear_system/2:
clear_system(BootPid,State) ->
Heart = get_heart(State#state.kernel),
shutdown_pids(Heart,BootPid,State),
unload(Heart).
shutdown_pids(Heart,BootPid,State) ->
Timer = shutdown_timer(State#state.flags),
catch shutdown(State#state.kernel,BootPid,Timer,State),
kill_all_pids(Heart), % Even the shutdown timer.
kill_all_ports(Heart),
flush_timout(Timer).
%%
%% Kill all existing pids in the system (except init and heart).
kill_all_pids(Heart) ->
case get_pids(Heart) of
[] ->
ok;
Pids ->
kill_em(Pids),
kill_all_pids(Heart) % Continue until all are really killed.
end.
kill_em([Pid|Pids]) ->
exit(Pid,kill),
kill_em(Pids);
kill_em([]) ->
ok.
看到这里得到两个结论:
1. 除了kernel和init之外的所有的进程都是以kill的形式干掉的。注意这里的kernel是个list,包含多了pid
2. 先shutdown所有的kernel进程,后kill掉其他进程,最终halt。shutdown的形式则是一条EXIT消息
shutdown([{heart,_Pid}|Kernel],BootPid,Timer,State) ->
shutdown(Kernel, BootPid, Timer, State);
shutdown([{_Name,Pid}|Kernel],BootPid,Timer,State) ->
shutdown_kernel_pid(Pid, BootPid, Timer, State),
shutdown(Kernel,BootPid,Timer,State);
shutdown(_,_,_,_) ->
true.
%%
%% A kernel pid must handle the special case message
%% {'EXIT',Parent,Reason} and terminate upon it!
%%
shutdown_kernel_pid(Pid, BootPid, Timer, State) ->
Pid ! {'EXIT',BootPid,shutdown},
shutdown_loop(Pid, Timer, State, []).
发送EXIT消息,shutdown_loop/4等待结果。
根据上一篇分析的结果,application_controller是kernel pids的一员,查看application_controller的源码:
terminate(Reason, S) ->
case application:get_env(kernel, shutdown_func) of
{ok, {M, F}} ->
catch M:F(Reason);
_ ->
ok
end,
foreach(fun({_AppName, Id}) when is_pid(Id) ->
exit(Id, shutdown),
receive
{'EXIT', Id, _} -> ok
end;
(_) -> ok
end,
S#state.running),
ets:delete(ac_tab).
S#state.running中保存了所有已经在运行的application,对于每一个application,都以exit(Id, shutdown)形式让其退出,并一直等待。
到了这里不用继续看源码大致也能理清思路了,结论:
init:stop
→ 向所有kernel进程发送shutdown Exit消息
→ application 依次关闭子sup树(等待)
→ kill所有其他的非kernel进程
对于我们的app,一般都是最后启动,最先停止,如此,在停止我们的app的时候所有的系统服务都还是可用的(分布式、mnesia等等)。

