erlang trap_exit标记

来源:转载

本篇博文分析设置了 trap_exit 标记为 true 的 gen_server 进程会如何处理收到的退出信号。

设置了 trap_exit 为 true 的进程,在收到 非kill 的退出信号时候,会把该信号转换为普通的消息,而不会退出;通过 gen_server:handle_msg/5 和 gen_server:handle_msg/6 函数可知,若进程自身发生错误的时候,即使设置了 trap_exit 为 true,进程最终也会调用 ?MODULE:terminate/2 进行退出。 

用简单的例子验证:

 1 -module(test_gen_server).

2

3 -behavior(gen_server).

4

5 %% apis

6 -export([

7 start/0,

8 start_link/0

9 ]).

10

11 %% callbacks

12 -export([

13 init/1,

14 handle_call/3,

15 handle_cast/2,

16 handle_info/2,

17 terminate/2,

18 code_change/3

19 ]).

20

21 %% 字符串宏

22 -define(str_concat(Format, Args), lists:flatten(io_lib:format(Format, Args))).

23

24 %% 日志宏

25 -define(err(Format, Args), error_logger:error_report(?str_concat("Module: ~p, Line:~p~n" ++ Format, [?MODULE, ?LINE | Args]))).

26 -define(info(Format, Args), io:format(?str_concat(Format ++ " in Module: ~p at Line: ~p~n", Args ++ [?MODULE, ?LINE]), [])).

27

28 %% 进程状态结构定义

29 -record(state, {}).

30

31 %% Apis -------------------------------

32 start() ->

33 gen_server:start({local, ?MODULE}, ?MODULE, [], []).

34

35 start_link() ->

36 gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).

37

38 %% Callbacks --------------------------

39 init([]) ->

40 ?info("gen_server: ~p init !", [?MODULE]),

41 erlang:process_flag(trap_exit, true),

42 {ok, #state{}}.

43

44 handle_call(_Msg, _From, State) ->

45 {reply, reply, State}.

46

47 handle_cast(_Msg, State) ->

48 {noreply, State}.

49

50 handle_info(Msg, State) ->

51 ?info("recv info msg: ~p !", [Msg]),

52 {noreply, State}.

53

54 terminate(normal, _State) ->

55 ?info("gen_server: ~p stopped, reason: normal", [?MODULE]);

56 terminate(Reason, _State) ->

57 ?err("gen_server: ~p stopped, reason: ~p", [?MODULE, Reason]).

58

59 code_change(_OldVsn, State, _Extra) ->

60 {ok, State}.

61

62 %% Privates ---------------------------

 

编译这个 test_gen_server 模块,然后在erlang shell里面做如下测试:

E:\Worksapce\erlang_test>erl -pa ebin/

Eshell V7.1 (abort with ^G)

1> {ok, Pid} = test_gen_server:start().

gen_server: test_gen_server init ! in Module: test_gen_server at Line: 34

{ok,<0.33.0>}

2> erlang:exit(Pid, stop).

recv info msg: {'EXIT',<0.31.0>,stop} ! in Module: test_gen_server at Line: 45

true

3> erlang:exit(Pid, shutdown).

recv info msg: {'EXIT',<0.31.0>,shutdown} ! in Module: test_gen_server at Line: 45

true

4> erlang:exit(Pid, any).

recv info msg: {'EXIT',<0.31.0>,any} ! in Module: test_gen_server at Line: 45

true

5> erlang:exit(Pid, kill).

true

从上面测试结果来看,在收到 kill 退出信号之前,test_gen_server 都把退出信号转换为普通消息,并通过51行的info宏打印出来了;

但在收到 kill 退出信号时,并没有 ?MODULE:terminate/2 函数的打印,因为 test_gen_server 收到 kill 信号后无条件的马上终止了,而没有执行 ?MODULE:terminate/2 函数。

把设置 trap_exit 进程标记的 41 行注释掉,编译并重新加载 test_gen_server 模块再进行测试:

1> {ok, Pid} = test_gen_server:start().

gen_server: test_gen_server init ! in Module: test_gen_server at Line: 35

{ok,<0.33.0>}

2> erlang:exit(Pid, normal).

true

3> erlang:is_process_alive(Pid).

true

4> erlang:exit(Pid, any).

true

5> erlang:is_process_alive(Pid).

false

6> {ok, Pid2} = test_gen_server:start().

gen_server: test_gen_server init ! in Module: test_gen_server at Line: 35

{ok,<0.39.0>}

7> erlang:exit(Pid2, kill).

true

8> erlang:is_process_alive(Pid2).

false

测试结果表明,向 test_gen_server 发送 非normal 外的任何退出消息, test_gen_server 进程都会终止,且不会调用 ?MODULE:terminate/2 函数。

通过看 supervisor:shutdown/2  函数可知,在子进程退出设置 非brutal_kill 时, supervisor 在结束子进程时,会先发一条 shutdown 退出消息给子进程并等待子进程退出完毕,若退出超时则再次发送一条 kill 的退出消息给子进程,让子进程无条件退出。

所以如果子进程没有设置 trap_exit 进程标记,则子进程会在则收到 shutdown  之后就直接无条件退出了而不调用 ?MODULE:terminate/2 函数;

而设置 trap_exit  的话,子进程调用 ?MODULE:terminate/2 并退出,其退出原因是 shutdown。

通过看 gen_server:decode_msg/8 函数可知,gen_server 进程在收到 Parent 的任何 {'EXIT', Parent, Reason} 消息的时候(如父进程发送的 {'EXIT', Parent, shutdown} 消息),最终都会调用 ?MODULE:terminate/2 函数进行退出。

分享给朋友:
您可能感兴趣的文章:
随机阅读: