自己看代码时做的笔记,比较乱,还没有整理

本文,主要是supervisor:start_link/3的完整流程

入口

supervisor:start_link/3 serpervisor:start_link/2

代码:

Supervisor.erl


-spec start_link(Module, Args) ->startlink_ret() when

Module :: module(),

Args :: term().

start_link(Mod, Args) ->

  gen_server:start_link(supervisor, {self, Mod, Args}, []).


-spec start_link(SupName, Module, Args)-> startlink_ret() when

SupName :: sup_name(),

Module :: module(),

Args :: term().

start_link(SupName, Mod,Args) ->

gen_server:start_link(SupName, supervisor, {SupName, Mod, Args}, []).



supervisor:start_link/3,调用gen_server:start_link/4

serpervisor:start_link/2调用gen_server:start_link/3

下面以常用的supervisor:start_link/3为例


代码:

gen_server.erl


start_link(Name, Mod, Args, Options) ->

gen:start(?MODULE, link, Name, Mod, Args, Options).



代码:

gen.erl


-spec start(module(), linkage(),emgr_name(), module(), term(), options()) ->

start_ret().


start(GenMod, LinkP, Name, Mod, Args,Options) ->

  case where(Name) of

undefined->

do_spawn(GenMod,LinkP, Name, Mod, Args, Options);

Pid->

{error, {already_started, Pid}}

end.



代码:

gen.erl


do_spawn(GenMod, link,Name, Mod, Args, Options) ->

  Time = timeout(Options),

proc_lib:start_link(?MODULE, init_it,

                           [GenMod,self(), self(), Name, Mod, Args, Options],

                           Time,

                           spawn_opts(Options));

do_spawn(GenMod, _, Name, Mod, Args,Options) ->

  Time = timeout(Options),

  proc_lib:start(?MODULE, init_it,

 [GenMod, self(), self, Name, Mod, Args,Options],

 Time,

 spawn_opts(Options)).



代码:

proc_lib.erl


-spec start_link(Module, Function, Args)-> Ret when

Module :: module(),

Function :: atom(),

Args :: [term()],

Ret :: term() | {error, Reason :: term()}.


start_link(M, F, A) when is_atom(M),is_atom(F), is_list(A) ->

  start_link(M, F, A, infinity).


-spec start_link(Module, Function, Args,Time) -> Ret when

Module :: module(),

Function :: atom(),

Args :: [term()],

Time :: timeout(),

Ret :: term() | {error, Reason :: term()}.


start_link(M, F, A, Timeout) whenis_atom(M), is_atom(F), is_list(A) ->

  Pid = ?MODULE:spawn_link(M, F, A),

  sync_wait(Pid, Timeout).


-spec start_link(Module, Function, Args,Time, SpawnOpts) -> Ret when

Module :: module(),

Function :: atom(),

Args :: [term()],

Time :: timeout(),

SpawnOpts :: [spawn_option()],

Ret :: term() | {error, Reason :: term()}.


start_link(M,F,A,Timeout,SpawnOpts)when is_atom(M), is_atom(F), is_list(A) ->

   Pid = ?MODULE:spawn_opt(M, F, A,ensure_link(SpawnOpts)),

sync_wait(Pid, Timeout).




代码:

proc_lib.erl


-spec spawn_opt(Fun, SpawnOpts) -> pid()when

Fun :: function(),

SpawnOpts :: [spawn_option()].


spawn_opt(F, Opts) when is_function(F)->

  Parent = get_my_name(),

  Ancestors = get_ancestors(),

  check_for_monitor(Opts),

  erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,F],Opts).


-spec spawn_opt(Node, Function, SpawnOpts)-> pid() when

Node :: node(),

Function :: function(),

SpawnOpts :: [spawn_option()].


spawn_opt(Node, F, Opts) whenis_function(F) ->

  Parent = get_my_name(),

  Ancestors = get_ancestors(),

  check_for_monitor(Opts),

  erlang:spawn_opt(Node, ?MODULE, init_p, [Parent,Ancestors,F], Opts).


-spec spawn_opt(Module, Function, Args,SpawnOpts) -> pid() when

Module :: module(),

Function :: atom(),

Args :: [term()],

SpawnOpts :: [spawn_option()].


spawn_opt(M, F, A,Opts) when is_atom(M), is_atom(F), is_list(A) ->

  Parent = get_my_name(),

  Ancestors = get_ancestors(),

  check_for_monitor(Opts),

erlang:spawn_opt(?MODULE, init_p,[Parent,Ancestors,M,F,A], Opts).


-spec spawn_opt(Node, Module, Function,Args, SpawnOpts) -> pid() when

Node :: node(),

Module :: module(),

Function :: atom(),

Args :: [term()],

SpawnOpts :: [spawn_option()].


spawn_opt(Node, M, F, A, Opts) whenis_atom(M), is_atom(F), is_list(A) ->

  Parent = get_my_name(),

  Ancestors = get_ancestors(),

  check_for_monitor(Opts),

erlang:spawn_opt(Node,?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).



代码:

erlang.erl


-spec spawn_opt(Module, Function, Args,Options) ->

  pid() | {pid(),reference()} when

Module :: module(),

Function :: atom(),

Args :: [term()],

Options :: [Option],

Option :: link | monitor

 | {priority, Level ::priority_level()}

 | {fullsweep_after, Number ::non_neg_integer()}

 | {min_heap_size, Size ::non_neg_integer()}

 | {min_bin_vheap_size, VSize ::non_neg_integer()}.

spawn_opt(M, F, A, Opts) ->

  case catch erlang:spawn_opt({M,F,A,Opts}) of

{'EXIT',{Reason,_}}->

erlang:error(Reason, [M,F,A,Opts]);

Res->

Res

  end.

最后调到erlang:spawn_opt/1,这个函数应该是erlang的内置函数(BIF  不知道这么叫对不对?),没有找到这个函数的源代码,只能通过文档来理解它是干什么滴






Erlang文档:


spawn_opt/2

Returns the pid of a new process startedby the application ofFunto the empty list[]. Otherwise works likespawn_opt/4.

If the optionmonitoris given, the newly created process will be monitored and both the pid andreference for the monitor will be returned.

spawn_opt/3

Returns the pid of a new process startedby the application ofFunto the empty list[]onNode. IfNodedoes not exist, a useless pid is returned. Otherwise works likespawn_opt/4.

spawn_opt/4

Works exactly likespawn/3, except that an extra option list is given when creating the process.

金山词霸的翻译:

酷似spawn / 3,除了一个额外的选项列表创建时的过程。(大概可以理解为,除了在创建进程时额外设置了一个参数列表,其他的和spawn/3的作用一样)


……(下面都是是对于option参数的介绍)



spawn(Module,Function, Args) -> pid()

·Module = Function = atom()

·Args = [term()]

Returns the pid of a new process startedby the application ofModule:FunctiontoArgs. The new process created will be placed in the system scheduler queue andbe run some time later.

error_handler:undefined_function(Module,Function, Args)is evaluated by the new process ifModule:Function/Aritydoes not exist (whereArityis the length ofArgs). The error handler can be redefined (seeprocess_flag/2). Iferror_handleris undefined, or the user has redefined the defaulterror_handlerits replacement is undefined, a failure with the reasonundefwill occur.


创建了一个进程,执行,Module: Function (Args),返回改进程pid




从文档中不难看出来,spawn_opt/4 这个函数,就是创建了一个进程,去执行一个函数





例子:


代码:

test_sup.erl

-module(test_sup).

-export([start_link/1]).

-export([init/1]).


start_link(N)->

supervisor:start_link({local,?MODULE},?MODULE,N).


init(N)->

io:format(“init  ~p~n”,[N]),

{ok,{{simple_one_for_one,10,10},[{test,{test,start_link,[N]},permanent,2000,worker,[test]}].



test.erl

-behaviour(gen_server).

-module(test).

-export([start_link/1,init/1]).

-export([handle_call/3,handle_cast/2,handle_info/2,terminate/2,code_change/3]).

-record(state,{}).

start_link(N)->

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


init([N])->

io:format(“testinit ~p~n”,[N]),

{ok,#state{}}.


handle_call(Request,From,State)->

Reply=ok,

{reply,Reply,State}.

handle_cast(Msg,State)->

{noreply,State}.

handle_info(Info,State)->

{noreply,State}.

terminate(Reason,State)->

ok.

code_change(OldVsn,State,Extra)->

{ok,State}.


例子,启了一个监控树,监控树下启了一个gen_server进程



参数传递及函数调用关系

test_sup:start_link(N) ->


supervisor:start_link({local,test_sup},test_sup,[N])->


gen_server:start_link({local,test_sup },supervisor, {{local,test_sup }, test_sup, [N] }, [])->


gen:start(gen_server, link, {local,test_sup}, supervisor, {{local,test_sup }, test_sup, [N] },[]).


gen:start/6 代码

start(GenMod, LinkP, Name, Mod, Args,Options) ->

  case where(Name) of

undefined->

do_spawn(GenMod,LinkP, Name, Mod, Args, Options);

Pid->

{error, {already_started, Pid}}

end.


àdo_spawn(gen_server,link, {local,test_sup }, supervisor, {{local,test_sup }, test_sup, [N] },[])


do_spawn代码

do_spawn(GenMod, link, Name, Mod, Args,Options) ->

  Time = timeout(Options),

  proc_lib:start_link(?MODULE, init_it,

[GenMod,self(), self(), Name, Mod, Args, Options],

Time,

spawn_opts(Options));


àproc_lib:start_link(

gen,

init_it,

[gen_server,self(),self(),{local,test_sup},supervisor,{{local,test_sup}, test_sup, [N] },[]],

Time,

[]).

注:Options=[] 所以spawn_opts(Options)=[]

函数proc_lib:start_link/5创建了一个进程,来执行传入 M:F(Args),对于本例子该进程执行的函数是gen:init_it(gen_server,self(),self(),{local,test_sup }, supervisor,{{local,test_sup }, test_sup, [N] },[])

下面是proc_lib:start_link/5的实现代码

start_link(M,F,A,Timeout,SpawnOpts) whenis_atom(M), is_atom(F), is_list(A) ->

  Pid = ?MODULE:spawn_opt(M, F, A, ensure_link(SpawnOpts)),

sync_wait(Pid,Timeout).

只是调用了本模块的spawn_opt/4函数,





spawn_opt(M, F, A, Opts) when is_atom(M),is_atom(F), is_list(A) ->

  Parent = get_my_name(),

  Ancestors = get_ancestors(),

  check_for_monitor(Opts),

  erlang:spawn_opt(?MODULE, init_p, [Parent,Ancestors,M,F,A], Opts).


M= gen,

F=init_it,

A=[ gen_server,self(),self(),{local,test_sup }, supervisor, {{local,test_sup}, test_sup, [N] },[]],

Opts=[]


最后调用erlang: spawn_opt(proc_lib,init_p,[Parent,Ancestors,M,F,A],[])

这个函数上面解释过了,它创建了一个进程,执行函数

proc_lib: init_p(Parent,Ancestors,M,F,A).

下面看proc_lib: init_p/5的实现代码

-spec init_p(pid(), [pid()], function())-> term().


init_p(Parent, Ancestors, Fun) whenis_function(Fun) ->

  put('$ancestors', [Parent|Ancestors]),

  {module,Mod} = erlang:fun_info(Fun, module),

  {name,Name} = erlang:fun_info(Fun, name),

  {arity,Arity} = erlang:fun_info(Fun, arity),

  put('$initial_call', {Mod,Name,Arity}),

  try

Fun()

  catch

Class:Reason->

exit_p(Class, Reason)

  end.


-spec init_p(pid(), [pid()], atom(),atom(), [term()]) -> term().


init_p(Parent, Ancestors,M, F, A) when is_atom(M), is_atom(F), is_list(A) ->

  put('$ancestors', [Parent|Ancestors]),

  put('$initial_call', trans_init(M, F, A)),

  init_p_do_apply(M, F, A).


init_p_do_apply(M, F, A) ->

  try

apply(M, F, A)

  catch

Class:Reason->

exit_p(Class, Reason)

end.

不难发现,proc_lib: init_p/5最终结果是执行了apply(M, F, A) 也就是M:F(A).

在例子中

M= gen,

F=init_it,

A=[ gen_server,self(),self(),{local,test_sup }, supervisor, {{local,test_sup}, test_sup, [N] },[]],

就是

gen:init_it(gen_server,self(),self(),{local,test_sup},supervisor, {{local,test_sup }, test_sup, [N] },[])

下面是gen:init_it/7的实现代码

%%-----------------------------------------------------------------

%% Initiate the new process.

%% Register the name using the Rfuncfunction

%% Calls the Mod:init/Args function.

%% Finally an acknowledge is sent to Parentand the main

%% loop is entered.

%%-----------------------------------------------------------------

init_it(GenMod, Starter, Parent, Mod, Args,Options) ->

  init_it2(GenMod, Starter, Parent, self(), Mod, Args, Options).


init_it(GenMod,Starter, Parent, Name, Mod, Args, Options) ->

  case name_register(Name) of

true->

init_it2(GenMod,Starter, Parent, Name, Mod, Args, Options);

{false,Pid} ->

proc_lib:init_ack(Starter, {error,{already_started, Pid}})

  end.


init_it2(GenMod, Starter, Parent, Name,Mod, Args, Options) ->

GenMod:init_it(Starter, Parent, Name, Mod, Args, Options).


从注释中就能看出来,init_it的功能:注册进程名,调用函数Mod:init/Args

例子中,注册的进程名是{local,test_sup },调用函数是gen_server:init_it/6

gen_server:init_it(Starter,Parent, {local,test_sup}, supervisor, {{local,test_sup }, test_sup, [N]},[])


gen_server:init_it/6 代码:

init_it(Starter, self, Name, Mod, Args,Options) ->

  init_it(Starter, self(), Name, Mod, Args, Options);

init_it(Starter,Parent, Name0, Mod, Args, Options) ->

  Name = name(Name0),

  Debug = debug_options(Name, Options),

case catch Mod:init(Args) of

{ok,State} ->

proc_lib:init_ack(Starter, {ok, self()}),        

loop(Parent, Name, State, Mod, infinity,Debug);

{ok,State, Timeout} ->

proc_lib:init_ack(Starter, {ok, self()}),        

loop(Parent, Name, State, Mod, Timeout,Debug);

{stop,Reason} ->

%% For consistency, we must make sure thatthe

%% registered name (if any) is unregisteredbefore

%% the parent process is notified about thefailure.

%% (Otherwise, the parent process could get

%% an 'already_started' error if itimmediately

%% tried starting the process again.)

unregister_name(Name0),

proc_lib:init_ack(Starter, {error,Reason}),

exit(Reason);

ignore->

unregister_name(Name0),

proc_lib:init_ack(Starter, ignore),

exit(normal);

{'EXIT',Reason} ->

unregister_name(Name0),

proc_lib:init_ack(Starter, {error,Reason}),

exit(Reason);

Else->

Error = {bad_return_value, Else},

proc_lib:init_ack(Starter, {error, Error}),

exit(Error)

end.



从代码中可以看出来Mod:init(Args) 的返回值很关键,例子中即

supervisorinit({{local,test_sup}, test_sup, [N] }).


下面看supervisor:init/1,代码实现

init({SupName, Mod, Args}) ->

  process_flag(trap_exit, true),

case Mod:init(Args) of

{ok,{SupFlags, StartSpec}} ->

 case init_state(SupName, SupFlags, Mod, Args)of

  {ok,State} when ?is_simple(State) ->

  init_dynamic(State, StartSpec);

  {ok,State} ->

  init_children(State, StartSpec);

  Error->

  {stop, {supervisor_data, Error}}

end;

ignore->

ignore;

Error->

{stop, {bad_return, {Mod, init, Error}}}

end.




例子中Mod:init(Args) test_sup:init(N),yahoooo!!!终于回到自己写的代码中了,看看它的返回值{ok,{{one_for_one,10,10},[{test,{test,start_link,[N]},permanent,2000,worker,[test] }],

很明显和{ok, {SupFlags, StartSpec}}匹配,下一步init_state(SupName, SupFlags, Mod, Args)

代码如下

init_state(SupName, Type, Mod, Args) ->

  case catch init_state1(SupName, Type, Mod, Args) of

{ok,State} ->

{ok, State};

Error->

Error

  end.


init_state1(SupName, {Strategy, MaxIntensity,Period}, Mod, Args) ->

  validStrategy(Strategy),

  validIntensity(MaxIntensity),

  validPeriod(Period),

  {ok, #state{name = supname(SupName,Mod),

  strategy= Strategy,

  intensity= MaxIntensity,

  period= Period,

  module= Mod,

  args= Args}};

init_state1(_SupName, Type, _, _) ->

  {invalid_type, Type}.


validStrategy(simple_one_for_one) ->true;

validStrategy(one_for_one)        -> true;

validStrategy(one_for_all)        -> true;

validStrategy(rest_for_one)       -> true;

validStrategy(What)               -> throw({invalid_strategy,What}).


validIntensity(Max) when is_integer(Max),

Max >=  0 -> true;

validIntensity(What)               -> throw({invalid_intensity,What}).


validPeriod(Period) when is_integer(Period),

Period > 0 ->true;

validPeriod(What)                   -> throw({invalid_period,What}).


supname(self, Mod) -> {self(), Mod};

supname(N, _)      -> N.


很容易理解,正常情况下返回值是这样的一个{ok,#state{}}

#state{name = supname(SupName,Mod),

  strategy= Strategy,

  intensity= MaxIntensity,

  period= Period,

  module= Mod,

  args= Args}}


看看例子中这个record应该是怎样的:

#state{name = {local,test_sup },

  strategy= one_for_one,

  intensity= 10,

  period= 10,

  module= test_sup,

  args= [N]}}

下面看下init_state(SupName, SupFlags, Mod, Args)的返回值匹配选项

supervisor.erl代码片段


-define(is_simple(State),State#state.strategy =:= simple_one_for_one).

….

{ok, State} when ?is_simple(State) ->

  init_dynamic(State, StartSpec);

  {ok,State} ->

  init_children(State, StartSpec);

……

……..

init_children(State, StartSpec) ->

  SupName = State#state.name,

  case check_startspec(StartSpec) of

  {ok, Children} ->

  case start_children(Children, SupName) of

{ok, NChildren} ->

{ok, State#state{children =NChildren}};

{error, NChildren, Reason}->

  terminate_children(NChildren, SupName),

{stop, {shutdown, Reason}}

  end;

  Error ->

  {stop, {start_spec, Error}}

  end.


init_dynamic(State, [StartSpec]) ->

  case check_startspec([StartSpec]) of

  {ok, Children} ->

{ok, State#state{children = Children}};

  Error ->

  {stop, {start_spec, Error}}

  end;

init_dynamic(_State, StartSpec) ->

{stop,{bad_start_spec, StartSpec}}.

…….


init_dynamic 不会创建子进程

init_children 会创建子进程start_children

如果State#state.strategy =:= simple_one_for_one  init_dynamic 不会创建子进程

反之init_children 会创建子进程(start_children(),详细的请参考文档,例子中为

One_for_one,是会创建子进程的,下面看start_children(Children,SupName) 代码


start_children(Children, SupName) -> start_children(Children, [], SupName).


start_children([Child|Chs],NChildren, SupName) ->

  case do_start_child(SupName, Child) of

{ok,undefined} when Child#child.restart_type =:= temporary ->

start_children(Chs, NChildren, SupName);

{ok,Pid} ->

start_children(Chs, [Child#child{pid =Pid}|NChildren], SupName);

{ok,Pid, _Extra} ->

start_children(Chs, [Child#child{pid =Pid}|NChildren], SupName);

{error,Reason} ->

report_error(start_error, Reason, Child,SupName),

{error, lists:reverse(Chs) ++ [Child |NChildren],

{failed_to_start_child,Child#child.name,Reason}}

  end;

start_children([], NChildren, _SupName)->

  {ok, NChildren}.


do_start_child(SupName, Child) ->

  #child{mfargs = {M, F, Args}} = Child,

  case catch apply(M, F, Args) of

{ok,Pid} when is_pid(Pid) ->

NChild = Child#child{pid = Pid},

report_progress(NChild, SupName),

{ok, Pid};

{ok,Pid, Extra} when is_pid(Pid) ->

NChild = Child#child{pid = Pid},

report_progress(NChild, SupName),

{ok, Pid, Extra};

ignore->

{ok, undefined};

{error,What} -> {error, What};

What-> {error, What}

  end.


do_start_child_i(M, F, A) ->

  case catch apply(M, F, A) of

{ok,Pid} when is_pid(Pid) ->

{ok, Pid};

{ok,Pid, Extra} when is_pid(Pid) ->

{ok, Pid, Extra};

ignore->

{ok, undefined};

{error,Error} ->

{error, Error};

What->

{error, What}

  end.


代码中start_children会调到do_start_child,然后会调apply(M,F,Args),好吧又一个,下面来看看MF Args都是什么

init_children(State, StartSpec) ::

State=#state{name = {local,test_sup },

  strategy= one_for_one,

  intensity= 10,

  period= 10,

  module= test_sup,

  args= [N]}}

StartSpec=[{test,{test,start_link,[N]},permanent,2000,worker,[test]}],


在函数中init_children/2中需要检查要创建的子进程参数,并返回一需要的结构,如下

代码片段:

case check_startspec(StartSpec) of

  {ok, Children} ->

  case start_children(Children, SupName) of

{ok, NChildren} ->

{ok, State#state{children =NChildren}};


……..



check_startspec(Children) ->check_startspec(Children, []).


check_startspec([ChildSpec|T], Res) ->

  case check_childspec(ChildSpec) of

{ok,Child} ->

case lists:keymember(Child#child.name,#child.name, Res) of

  true-> {duplicate_child_name, Child#child.name};

  false-> check_startspec(T, [Child | Res])

end;

Error-> Error

  end;

check_startspec([], Res) ->

  {ok, lists:reverse(Res)}.


check_childspec({Name, Func, RestartType,Shutdown, ChildType, Mods}) ->

  catch check_childspec(Name, Func, RestartType, Shutdown, ChildType,Mods);

check_childspec(X) ->{invalid_child_spec, X}.


check_childspec(Name, Func, RestartType,Shutdown, ChildType, Mods) ->

  validName(Name),

  validFunc(Func),

  validRestartType(RestartType),

  validChildType(ChildType),

  validShutdown(Shutdown, ChildType),

  validMods(Mods),

  {ok, #child{name = Name, mfargs = Func, restart_type = RestartType,

  shutdown= Shutdown, child_type = ChildType, modules = Mods}}.



check_startspec/1正常情况下,返回一个[# child{}]列表



-record(child, {% pid is undefined whenchild is not running

pid = undefined :: child()

 | {restarting, pid() | undefined}

 | [pid()],

  name            :: child_id(),

  mfargs          :: mfargs(),

  restart_type    :: restart(),

  shutdown        :: shutdown(),

  child_type      :: worker(),

  modules= []    :: modules()}).

例子中返回的值是[#child{name = test, mfargs = {test,start_link,[N]}, restart_type = permanent,

  shutdown= 2000, child_type = worker, modules = [test]}]


start_children(Children, SupName):

Children=[#child{name = test, mfargs = {test,start_link,[N]},restart_type = permanent,shutdown = 2000, child_type = worker, modules = [test]}]

SupName=test_sup


Do_start_child(SupName,Children)

{M,F,A}= Children#child.mfargs= {test,start_link,[N]},


apply(test,start_link,[N])


OOOOOOO!!!!!!!!!!!!

大功告成!!!!!