Erlang退出/ R15B03中的2个错误

时间:2012-11-19 17:40:14

标签: erlang erlang-shell

here的简短副本:

  

exit(Pid, Reason) -> true

     

类型:

     

Pid = pid()   原因= term()

     

将退出原因Reason的退出信号发送到流程Pid

     

如果“原因”是除normalkill以外的任何字词,则会应用以下行为:

     

如果Pid没有捕获退出,则Pid本身将以退出原因Reason退出。如果Pid正在捕获退出,则退出信号将转换为消息{'EXIT', From, Reason}并传递到Pid的消息队列。 From是发送退出信号的进程的pid。另请参阅process_flag/2

     

如果Reason是原子normalPid将不会退出。如果它正在捕获退出,则退出信号将转换为消息{'EXIT', From, normal}并传递到其消息队列。

     

如果Reason是原子kill,即如果调用了exit(Pid, kill),则会向Pid发送无法匹配的退出信号,该信号将无条件退出并退出原因{{ 1}}。

killed用作exit/2self()用作Pid时,我正在使用normal函数及其行为。

Reason

是不是只有'正常'退出消息被发送到shell进程,所以没有理由退出?

类似地:

Erlang R15B03 (erts-5.9.3) [source] [64-bit] [smp:8:8] [async-threads:0] [hipe] [kernel-poll:false]

Eshell V5.9.3  (abort with ^G)
1> self().
<0.32.0>
2> exit(self(), normal).
** exception exit: normal
3> self().
<0.35.0>

可是:

4> spawn(fun() -> receive Pid -> Pid ! ok end end). 
<0.38.0>
5> exit(v(4), normal).
true
6> v(4) ! self().
<0.35.0>
7> flush().
Shell got ok
ok

5 个答案:

答案 0 :(得分:3)

看起来Erlang shell(shell.erl)不会处理类型'EXIT'的{​​{1}}消息,与其他退出消息的处理方式不同,这意味着它会发送错误并重新启动贝壳。如果您真的想知道这一点,您可以使用debugger以这种方式跟踪程序流程:

  1. 下载shell.erl
  2. 将文件名更改为normal
  3. 打开文件并将模块名称也更改为shell2.erl。您需要执行此操作,因为编译器会抱怨shell2位于粘滞目录中。
  4. 启动shell提示。
  5. erl
  6. c(shell2, [debug_info]).
  7. debugger:start().并选择shell2.erl
  8. Module -> Interpret
  9. 追踪!

答案 1 :(得分:1)

我认为可以从源代码'stdlib-1.18.2 / src / shell.erl'找到原因。

get_command(Prompt, Eval, Bs, RT, Ds) ->
    Parse = fun() -> exit(io:parse_erl_exprs(Prompt)) end,
    Pid = spawn_link(Parse),
    get_command1(Pid, Eval, Bs, RT, Ds).

get_command1(Pid, Eval, Bs, RT, Ds) ->
    receive
    {'EXIT', Pid, Res} ->
        {Res, Eval};
    {'EXIT', Eval, {Reason,Stacktrace}} ->
            report_exception(error, {Reason,Stacktrace}, RT),
        get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds);
    {'EXIT', Eval, Reason} ->
            report_exception(error, {Reason,[]}, RT),
        get_command1(Pid, start_eval(Bs, RT, Ds), Bs, RT, Ds)
    end.

report_exception(Class, Reason, RT) ->
    report_exception(Class, serious, Reason, RT).

report_exception(Class, Severity, {Reason,Stacktrace}, RT) ->
    Tag = severity_tag(Severity),
    I = iolist_size(Tag) + 1,
    PF = fun(Term, I1) -> pp(Term, I1, RT) end,
    SF = fun(M, _F, _A) -> (M =:= erl_eval) or (M =:= ?MODULE) end,
    io:requests([{put_chars, Tag},
                 {put_chars, 
                  lib:format_exception(I, Class, Reason, Stacktrace, SF, PF)},
                 nl]).

start_eval(Bs, RT, Ds) ->
    Self = self(),
    Eval = spawn_link(fun() -> evaluator(Self, Bs, RT, Ds) end), %%<========start a new shell pid
    put(evaluator, Eval),
    Eval.

severity_tag(fatal)   -> <<"*** ">>;
severity_tag(serious) -> <<"** ">>;
severity_tag(benign)  -> <<"* ">>.

对于你的第一种情况(向self()发送消息),信号符合get_command1/5的第二种情况,它将首先给出错误消息,并产生一个新的shell pid。请注意start_eval功能。

对于你的第二种情况(向产生pid发送消息),回到关于exit功能的帖子的第一部分,这是合乎逻辑的。您生成的pid不会捕获退出消息,并忽略(normal) exit消息。因此shell只有在您的生成pid实际退出时才会收到退出消息。它将进入get_command1/5的第一个条件。由于未调用start_evelshell pid将保持不变。

对于你的第三种情况,我不知道为什么。我还认为is_process_alive(v(8))应该返回true。

答案 2 :(得分:1)

正如您的第三个示例所示,如果任何进程执行exit(self(), normal),那么它会崩溃,而执行exit(AnotherPid, normal)时不会导致其他进程崩溃。我在R15B上验证了它。我个人认为这是一个错误,因为将退出信号normal发送到任何进程不应导致其崩溃。

答案 3 :(得分:0)

对于第三种情况,这里的关键点是self()是生成进程的pid而不是shell的pid。

请参阅以下代码:

Eshell V5.9  (abort with ^G)

1&GT;自()。

&LT; 0.32.0&GT;

2 - ; spawn(fun() - &gt; io:format(“这是〜p~n”,[self()]),exit(self(),normal),receive _ - &gt; ok end end)。

这是<0.35.0&gt; &LT; 0.35.0&GT;

3&GT; is_process_alive(V(2))。

4&GT;

答案 4 :(得分:0)

在你引用的文档中就可以了:

If Pid is not trapping exits, Pid itself will exit with exit reason Reason.

如果您没有捕获退出,那么您的流程将退出。

1> self().
<0.32.0>
2> process_flag(trap_exit, true).
false
3> exit(self(), normal).
true
4> self().
<0.32.0>
5> flush().             
Shell got {'EXIT',<0.32.0>,normal}
ok

如果您没有捕获退出,则不会发送有关退出的“消息”。这个过程就好了。与任何与之相关的过程也是如此。这是trap_exit的用途。