【问题标题】:Erlang, process_flag trap_exit kills my gen_server from the CLIErlang,process_flag trap_exit 从 CLI 杀死我的 gen_server
【发布时间】:2011-12-06 07:48:52
【问题描述】:

我有这个正在使用的 gen_server:

-module(user_info_provider).
-export([start_link/0, stop/0]).
-export([init/1, terminate/2, handle_info/2, handle_call/3, handle_cast/2,
 code_change/3]).
-export([request_user_info/2]).

-behaviour(gen_server).

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

stop() ->
    gen_server:cast(?MODULE, stop).

request_user_info(From,UserId) ->
    gen_server:cast(?MODULE, {request_user_info, From, UserId}).
%% Callback Functions
init(_) ->
    process_flag(trap_exit, true),
    io:format("I am ~w ~n", [self()]),
    {ok, null}.
%% @doc terminate
terminate(Reason, _LoopData) ->
    io:format("Terminating by ~w~n",[Reason]),
    {ok, null}.
handle_cast({request_user_info,From,UserId}, LoopData) ->
    {noreply, LoopData};
handle_cast(stop, LoopData) ->
    {stop, normal, LoopData}.
handle_info(_Info, State) ->
    {ok, State}.
code_change(_OldVsn, State, _Extra) ->
    {ok, State}.
handle_call(_,_From,LoopData) ->
    {ok,ok,LoopData}.

问题是,正如我接下来展示的那样,如果我像 erl -pa ebin/ -s user_info_provider start_link 这样从 cli 执行它,它会立即死掉,但我可以从控制台生成它并且它可以工作。

erl -pa ebin -s user_info_provider start_link
Erlang R14B02 (erts-5.8.3) [source] [smp:4:4] [rq:4] [async-threads:0] [kernel-poll:false]

I am <0.32.0> 
Terminating by normal
Eshell V5.8.3  (abort with ^G)
1> user_info_provider:start_link().
I am <0.35.0> 
{ok,<0.35.0>}

如果我没有设置 process_flag(trap_exit, true) 或者我没有使用 -s module function 直接从控制台启动它,则不会发生这种情况。
我这样启动它是因为真正的 gen_server 要复杂得多,我正在通过 Makefile 调用独立测试它。
有什么想法吗?

【问题讨论】:

    标签: linux erlang command-line-interface gen-server


    【解决方案1】:

    解决方案是W55tKQbuRu28Q4xv 建议的解决方案,或者不使用start_link,仅使用start。这是发生了什么:

    -s 参数由init 处理。粗略地说,init 将 spawn 一个新进程,然后它会使用它来初始化和运行所有 -s 参数。之后,这个生成的进程将退出。

    由于 init 进程退出,并且您捕获退出,您的进程会收到一条消息 {'EXIT', P, Reason},其中 P 是由 init 生成的进程的 pid()。此消息由进程的gen_server 部分处理。通常这样的消息会被转发到您的handle_info/2 回调(顺便说一句,它在您的代码中有错误的返回值,应该是 noreply)。但在这种情况下,它将不会被转发。原因是gen_server 包含其 进程的概念。它通过进程字典和proc_lib 放在那里的值'$ancestors' 来记录哪个进程产生了它。现在,如果退出消息从 parent 到达,则立即调用终止回调并终止进程。这样的消息将不会转发给您。这就是你所看到的。

    解决方案,漂亮的解决方案是创建一个小型应用程序,一个主管,并将您的流程置于该主管之下。然后从 -s 调用application:start(your_app)。之所以可行,是因为应用程序控制器是单独运行的。更好的解决方案是构建一个可以自动启动您的应用程序的版本。发布是您的应用程序 + 它的依赖项与 ERTS 运行时捆绑在一起。这样的版本完全独立存在,可以复制到目标主机并运行。因此,在目标系统上不需要 Erlang,因为该版本是自包含的。

    【讨论】:

    • 很好的答案@"I GIVE CRAP ANSWERS" :D,看起来我又一次忘记了 erlang 实际上几乎是一个操作系统,一切都是一个生死攸关的过程。
    【解决方案2】:

    用supervisor做最小的应用程序(rebar可以生成这样的应用程序的骨架)并用-s从cli运行它。

    【讨论】:

      猜你喜欢
      • 2011-10-06
      • 2023-03-17
      • 2014-12-05
      • 2020-01-10
      • 2011-10-08
      • 2011-08-09
      • 2014-11-09
      • 2012-02-11
      • 1970-01-01
      相关资源
      最近更新 更多