2017-06-01 97 views
0

我試圖啓動一個主管和一個gen_server進程,同時一個節點從命令行創建的,我用下面的命令來啓動節點:無法啓動過程

erl -name [email protected] -s ets_sup start [email protected] calc 

但是,當我使用whereis檢查新創建的節點上的進程時,我發現進程爲undefined。我沒有直接在節點外殼上運行ets_sup和ets_srv,但從命令行啓動節點不起作用。我想知道爲什麼會發生這種情況?

ets_sup.erl:

-module(ets_sup). 
-behaviour(supervisor). 
-export([start/1, start_link/1, init/1]). 

start([A, B]) -> 
     start_link([A, B]). 


start_link([A, B]) -> 
     supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]). 

init([A, B]) -> 
     {ok, {{one_for_all, 0, 1}, 
       [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}. 

ets_srv.erl:

-module(ets_srv). 
-behaviour(gen_server). 
-compile(export_all). 

-record(state, {a, b}). 

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

init([A, B]) -> 
     {ok, #state{a = A, b = B}}. 

check_sys() -> 
     gen_server:call(?MODULE, check_sys). 

handle_call(check_sys, _From, #state{a = A, b = B} = State) -> 
     {reply, {A, B}, State}. 

handle_info(_Info, State) -> {noreply, State}. 

handle_cast(_Req, State) -> {noreply, State}. 

code_change(_Ol, State, _Ex) -> {ok, State}. 

terminate(_R, _S) -> ok. 

回答

1

我認爲你真正開始你的參數列表功能ets_sup:start/1。您可以通過在ets_sup:start_link/1之前添加io:format(...)來驗證它。

但是執行功能ets_sup:start/1的過程很早就會因爲shutdown而很快死亡,而當您的主管與其鏈接時,它也會與其所有子女一起死亡。

您必須從不會死的進程調用此函數(通常,它是應用程序管理器的角色)。例如,做到:

start([A, B]) -> 
    % spawn a new process 
    spawn(fun() -> 
       start_link([A, B]), 
       % add a loop to keep it alive 
       loop() 
       end). 


loop() -> 
    receive 
     stop -> ok; 
     _ -> loop() 
    end. 

編輯,但不是一個答案

我已修改了代碼:

  • 在init服務器添加process_flag(trap_exit, true),,爲了趕退出消息,
  • 在服務器終止函數中添加io:format("server terminate with reason ~p, process ~p~n",[_R,self()]),以打印最終由主管發送的退出原因(注意:如果a n退出消息由另一個進程發送,handle_info將被觸發)。
  • 在監督器中添加ets_srv:check_sys(),剛好在服務器啓動之後,以檢查它是否確實正確地顯示了星號。

這裏是修改後的代碼。

-module(ets_sup). 
-behaviour(supervisor). 
-export([start/1, start_link/1, init/1]). 

start([A, B]) -> 
    start_link([A, B]). 


start_link([A, B]) -> 
    supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]), 
    ets_srv:check_sys(). 

init([A, B]) -> 
    {ok, {{one_for_all, 0, 1}, 
      [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}. 

-module(ets_srv). 
-behaviour(gen_server). 
-compile(export_all). 

-record(state, {a, b}). 

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

init([A, B]) -> 
    process_flag(trap_exit, true), 
    {ok, #state{a = A, b = B}}. 

check_sys() -> 
    gen_server:call(?MODULE, check_sys). 

handle_call(check_sys, _From, #state{a = A, b = B} = State) -> 
    io:format("check_sys state ~p, process ~p~n",[State,self()]), 
    {reply, {A, B}, State}. 

handle_info(_Info, State) -> 
    {noreply, State}. 

handle_cast(_Req, State) -> 
    {noreply, State}. 

code_change(_Ol, State, _Ex) -> 
    {ok, State}. 

terminate(_R, _S) -> 
    io:format("server terminate with reason ~p, process ~p~n",[_R,self()]), 
    ok. 

運行此版本顯示,管理程序正確啓動服務器,然後向其發送關閉消息。如果主管在shell中啓動,則不會發生這種情況。

C:\src>erl -s ets_sup start [email protected] calc 
check_sys state {state,'[email protected]',calc}, process <0.56.0> 
server terminate with reason shutdown, process <0.56.0> 
Eshell V8.2 (abort with ^G) 
1> whereis(ets_srv). 
undefined 
2> ets_sup:start(['[email protected]',calc]). 
check_sys state {state,'[email protected]',calc}, process <0.61.0> 
{'[email protected]',calc} 
3> whereis(ets_srv). 
<0.61.0> 
4> ets_srv:check_sys(). 
check_sys state {state,'[email protected]',calc}, process <0.61.0> 
{'[email protected]',calc} 
5> exit(whereis(ets_srv),shutdown). 
true 
6> whereis(ets_srv). 
<0.61.0> 
7> exit(whereis(ets_srv),kill). 
** exception exit: shutdown 
8> whereis(ets_srv). 
undefined 
9> 

我已經驗證了,如果你使用spawn_link啓動普通程序(不是管理器)以同樣的方式,它不接受任何退出消息。

-module (st). 

-compile([export_all]). 

start(Arg) -> 
    do_start(Arg). 

do_start(Arg) -> 
    io:format("spawn from ~p~n",[self()]), 
    register(?MODULE,spawn_link(fun() -> init(Arg) end)). 

init(Arg) -> 
    io:format("init with ~p in ~p~n",[Arg,self()]), 
    process_flag(trap_exit, true), 
    Pid = self(), 
    spawn(fun() -> monitor(process,Pid), receive M -> io:format("loop received ~p~n",[M]) end end), 
    loop(Arg). 

loop(Arg) -> 
    receive 
     state -> 
      io:format("state is ~p~n",[Arg]), 
      loop(Arg); 
     stop -> 
      io:format("stopping~n"); 
     _ -> 
      loop(Arg) 
    end. 

執行給:

C:\src>erl -s st start [email protected] calc 
spawn from <0.3.0> 
init with ['[email protected]',calc] in <0.55.0> 
Eshell V8.2 (abort with ^G) 
1> whereis(st). 
<0.55.0> 
2> exit(whereis(st),shutdown). 
true 
3> whereis(st). 
<0.55.0> 
4> st ! state. 
state is ['[email protected]',calc] 
state 
5> st ! stop. 
stopping 
loop received {'DOWN',#Ref<0.0.4.66>,process,<0.55.0>,normal} 
stop 
6> whereis(st). 
undefined 
7> 

編輯,另一種方式來「分離」主管

我所做的表明,主管接收到關閉消息的測試。我不知道爲什麼,通常我會使用應用程序機制來啓動一個監督樹,並且我從未遇到過這種情況。

我建議你從其父斷開鏈接的主管,所以不會收到一個關機消息:

-module(ets_sup). 
-behaviour(supervisor). 
-export([start/1, start_link/1, init/1]). 

start([A, B]) -> 
    start_link([A, B]). 


start_link([A, B]) -> 
    supervisor:start_link({local, ?MODULE}, ?MODULE, [A, B]), 
    ets_srv:check_sys(). 

init([A, B]) -> 
    {links,[Parent]} = process_info(self(),links), 
    unlink(Parent), 
    {ok, {{one_for_all, 0, 1}, 
      [{ets_srv, {ets_srv, start_link, [A, B]}, permanent, 5000, worker, [ets_srv]}]}}. 

的現在,它的工作原理:

C:\src>erl -s ets_sup start [email protected] calc 
check_sys state {state,'[email protected]',calc}, process <0.56.0> 
Eshell V8.2 (abort with ^G) 
1> ets_srv:check_sys(). 
check_sys state {state,'[email protected]',calc}, process <0.56.0> 
{'[email protected]',calc} 
2>