admin管理员组文章数量:1794759
trap
今天写代码的发现,关闭app的时候,监控树下的某个进程terminate函数没执行,所以有以下备忘记录
1 erlang:process_flag(trap_exit,true).
When trap_exit is set to true, exit signals arriving to a process are converted to {'EXIT', From, Reason} messages, which can be received as ordinary messages. If trap_exit is set to false, the process exits if it receives an exit signal other than normal and the exit signal is propagated to its linked processes. Application processes are normally not to trap exits.
可以理解为捕获退出信号:如果trap_exit设置为true,该进程接收到的退出信号被转为{'EXIT', From, Reason}格式的消息;trap_exit为false,如果接收到除normal之外的退出信号,则该进程退出并将退出信号传递给跟它连接的进程。
2 erlang:exit(Pid, Reason)
Sends an exit signal with exit reason Reason to the process or port identified by Pid.
The following behavior applies if Reason is any term, except normal or kill:
If Pid is not trapping exits, Pid itself exits with exit reason Reason.
If Pid is trapping exits, the exit signal is transformed into a message{'EXIT', From, Reason} and delivered to the message queue of Pid.
From is the process identifier of the process that sent the exit signal. See also process_flag/2.
If Reason is the atom normal, Pid does not exit. If it is trapping exits, the exit signal is transformed into a message {'EXIT', From, normal} and delivered to its message queue.
If Reason is the atom kill, that is, if exit(Pid, kill) is called, an untrappable exit signal is sent to Pid, which unconditionally exits with exit reason killed.
大概理解为:
A 如果退出Reason是除了normal和kill之外的任意term:
1:如果trap_exit=false Pid进程退出
2:如果trap_exit=true Pid进程捕获退出信号,退出信号转化格式为{'EXIT', From, Reason}的一条普通消息
B
1:如果Reason=normal, pid进程不会退出。 如果trap_exit=true Pid进程捕获退出信号,退出信号转化格式为{'EXIT', From, Reason}的一条普通消息
2:如果Reason=kill,pid进程无条件退出,无论是否设置trap_exit=true;也就是说,trap_exit无法捕获kill的退出信号。
3 terminate:进程退出前的数据保存操作可以在这里进行。
一般情况下,自己写的进程停止操作都是如下:
stop(Pid) when is_pid(Pid) ->
gen_server:call(Pid, stop).
handle_call(stop, State) ->
{reply, stop, normal, State};
但是,监控树下的进程停止操作是执行erlang:exit(Pid,Reason)的,具体代码可以看一下:supervisor.erl的源码
%%----------------------------------------------------------------- %% Func: terminate_children/2 %% Args: Children = [child_rec()] in termination order %% SupName = {local, atom()} | {global, atom()} | {pid(),Mod} %% Returns: NChildren = [child_rec()] in %% startup order (reversed termination order) %%----------------------------------------------------------------- terminate_children(Children, SupName) ->terminate_children(Children, SupName, []).%% Temporary children should not be restarted and thus should %% be skipped when building the list of terminated children, although %% we do want them to be shut down as many functions from this module %% use this function to just clear everything. terminate_children([Child = #child{restart_type=temporary} | Children], SupName, Res) ->_ = do_terminate(Child, SupName), % 顺序停止子进程terminate_children(Children, SupName, Res); terminate_children([Child | Children], SupName, Res) ->NChild = do_terminate(Child, SupName),terminate_children(Children, SupName, [NChild | Res]); terminate_children([], _SupName, Res) ->Res.do_terminate(Child, SupName) when is_pid(Child#child.pid) ->case shutdown(Child#child.pid, Child#child.shutdown) ofok ->ok;{error, normal} when Child#child.restart_type =/= permanent ->ok;{error, OtherReason} ->report_error(shutdown_error, OtherReason, Child, SupName)end,Child#child{pid = undefined}; do_terminate(Child, _SupName) ->Child#child{pid = undefined}.%%----------------------------------------------------------------- %% Shutdowns a child. We must check the EXIT value %% of the child, because it might have died with another reason than %% the wanted. In that case we want to report the error. We put a %% monitor on the child an check for the 'DOWN' message instead of %% checking for the 'EXIT' message, because if we check the 'EXIT' %% message a "naughty" child, who does unlink(Sup), could hang the %% supervisor. %% Returns: ok | {error, OtherReason} (this should be reported) %%----------------------------------------------------------------- shutdown(Pid, brutal_kill) -> %如果子进程的重启策略是brutal_kill case monitor_child(Pid) ofok ->exit(Pid, kill), % 则子进程无法执行terminate函数,因为kill退出信号无法捕获为{'EXIT', From, Reason}receive{'DOWN', _MRef, process, Pid, killed} ->ok;{'DOWN', _MRef, process, Pid, OtherReason} ->{error, OtherReason}end;{error, Reason} -> {error, Reason}end; shutdown(Pid, Time) ->case monitor_child(Pid) ofok ->exit(Pid, shutdown), %% Try to shutdown gracefully % 其他重启策略的子进程退出信号receive {'DOWN', _MRef, process, Pid, shutdown} ->ok;{'DOWN', _MRef, process, Pid, OtherReason} ->{error, OtherReason}after Time ->exit(Pid, kill), %% Force termination.receive{'DOWN', _MRef, process, Pid, OtherReason} ->{error, OtherReason}endend;{error, Reason} -> {error, Reason}end.%% Help function to shutdown/2 switches from link to monitor approach monitor_child(Pid) ->%% Do the monitor operation first so that if the child dies %% before the monitoring is done causing a 'DOWN'-message with%% reason noproc, we will get the real reason in the 'EXIT'-message%% unless a naughty child has already done unlink...erlang:monitor(process, Pid),unlink(Pid),receive%% If the child dies before the unlik we must empty%% the mail-box of the 'EXIT'-message and the 'DOWN'-message.{'EXIT', Pid, Reason} -> receive {'DOWN', _, process, Pid, _} ->{error, Reason}endafter 0 -> %% If a naughty child did unlink and the child dies before%% monitor the result will be that shutdown/2 receives a %% 'DOWN'-message with reason noproc.%% If the child should die after the unlink there%% will be a 'DOWN'-message with a correct reason%% that will be handled in shutdown/2. ok end.
为什么trap_exit=true,进程退出之前会跑到terminate函数呢?看一下gen_server.erl的源码:
decode_msg(Msg, Parent, Name, State, Mod, Time, HibernateAfterTimeout, Debug, Hib) ->case Msg of{system, From, Req} ->sys:handle_system_msg(Req, From, Parent, ?MODULE, Debug,[Name, State, Mod, Time, HibernateAfterTimeout], Hib);{'EXIT', Parent, Reason} ->terminate(Reason, ?STACKTRACE(), Name, undefined, Msg, Mod, State, Debug);_Msg when Debug =:= [] ->handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout);_Msg ->Debug1 = sys:handle_debug(Debug, fun print_event/3,Name, {in, Msg}),handle_msg(Msg, Parent, Name, State, Mod, HibernateAfterTimeout, Debug1)end.
本文标签: trap
版权声明:本文标题:trap 内容由林淑君副主任自发贡献,该文观点仅代表作者本人, 转载请联系作者并注明出处:http://www.xiehuijuan.com/baike/1695348992a308741.html, 本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌抄袭侵权/违法违规的内容,一经查实,本站将立刻删除。
发表评论