erlang的启动引导部分代码,本文章讲解的是排除C源码,从erlang代码开始的部分,C代码部分以后补充,

otp_ring0:start/2为第一个函数调用,源码内容很简单,我就不贴出来了,就是一个调用init:boot/0的功能。

接下来,我们分析init:boot/0的流程

init() ->
    register(init, self()),%将本进程注册为init进程
    process_flag(trap_exit, true),
    start_on_load_handler_process(), %spawn一个?ON_LOAD_HANDER进程,但并没有link
    {Start0,Flags,Args} = parse_boot_args(BootArgs),%解析启动参数神马的
    Start = map(fun prepare_run_args/1, Start0),
    Flags0 = flags_to_atoms_again(Flags),
    boot(Start,Flags0,Args).

这段代码主要有两个功能:

1.在start_on_load_handler_process函数中,启动一个注册名为init__boot__on_load_handler的进程,这个进程从代码上看是负责载入相关模块的一些操作,但我看了一下只有在启动参数-mode minimal的时候,kernel才会往该进程发送消息,起到作用,这点我有些不是很明白,但这些与正常boot过程影响不大,暂且搁下,以后再详细分析。

2.解析命令行参数(参数分析这段请查看我的日志http://www.cnblogs.com/star-star/archive/2012/12/26/2834194.html)

分割完参数后,进入boot/3,我们查看一下以下代码

boot(Start,Flags,Args) ->
%进入do_boot函数中会spawn_link一个启动脚本解析执行进程 BootPid = do_boot(Flags,Start),
%准备boot_loop时的上下文环境 State = #state{flags = Flags, args = Args, start = Start, bootpid = BootPid},
%进入init进程主循环 boot_loop(BootPid,State).
%接下来我们查看一下do_boot函数中的代码
do_boot(Flags,Start) -> Self = self(), spawn_link(fun() -> do_boot(Self,Flags,Start) end). do_boot(Init,Flags,Start) -> process_flag(trap_exit,true), {Pgm0,Nodes,Id,Path} = prim_load_flags(Flags), %根据启动参数,确定下从网络还是从本地加载文件的方式 Root = b2s(get_flag('-root',Flags)), PathFls = path_flags(Flags), Pgm = b2s(Pgm0), _Pid = start_prim_loader(Init,b2a(Id),Pgm,bs2as(Nodes), bs2ss(Path),PathFls), BootFile = bootfile(Flags,Root), BootList = get_boot(BootFile,Root), %从启动脚本中获得信息 LoadMode = b2a(get_flag('-mode',Flags,false)), Deb = b2a(get_flag('-init_debug',Flags,false)), %根据启动参数的-init_debug项来确定是否打印出过程信息 catch ?ON_LOAD_HANDLER ! {init_debug_flag,Deb}, BootVars = get_flag_args('-boot_var',Flags), ParallelLoad = (Pgm =:= "efile") and (erlang:system_info(thread_pool_size) > 0), %这里有一个优化,可以根据系统参数,确定是否采取并行加载的方式, PathChoice = code_path_choice(), eval_script(BootList,Init,PathFls,{Root,BootVars},Path, {true,LoadMode,ParallelLoad},Deb,PathChoice), %% To help identifying Purify windows that pop up, %% print the node name into the Purify log. (catch erlang:system_info({purify, "Node: " ++ atom_to_list(node())})), start_em(Start). %这里面就是执行启动参数指定启动的应用啊,执行的脚本神马的

do_boot函数的作用其实很简单,启动一个新进程,这个进程负责,加载启动脚本,在此过程中,不断像init主进程和开始创建的init__boot__on_load_handler,发送message通知,

根据启动脚本来解析执行,(这段请参考我的这篇日志http://www.cnblogs.com/star-star/archive/2012/12/27/2836074.html),

完毕后,执行start_em, 该函数是根据启动参数中的-run -s -eval等参数执行相应代码。

接下来我们看一下init主循环, boot_loop的代码。

进入boot_loop主循环,关于init进程我要说明一点,其实他有两个状态,一个循环在boot_loop中,另一个,以上文中所说的do_boot函数中启动boot脚本执行进程的正常终止为条件,

转换到loop函数中,也就是从引导状态,转为正常程序运行状态。

接下来我们来看一下init主循环代码。

boot_loop(BootPid, State) ->
    receive
    {BootPid,loaded,ModLoaded} ->
        Loaded = State#state.loaded,
        boot_loop(BootPid,State#state{loaded = [ModLoaded|Loaded]});
    {BootPid,started,KernelPid} ->
        boot_loop(BootPid, new_kernelpid(KernelPid, BootPid, State));
    {BootPid,progress,started} ->
            {InS,_} = State#state.status,
        notify(State#state.subscribed),
        boot_loop(BootPid,State#state{status = {InS,started},
                      subscribed = []});
    {BootPid,progress,NewStatus} ->
            {InS,_} = State#state.status,
        boot_loop(BootPid,State#state{status = {InS,NewStatus}});
    {BootPid,{script_id,Id}} ->
        boot_loop(BootPid,State#state{script_id = Id});
  %%以上的message都是在do_boot按启动脚本执行时,发送的通知消息
{
'EXIT',BootPid,normal} -> {_,PS} = State#state.status, notify(State#state.subscribed),
     %这里其实就是主循环的状态迁移了 loop(State#state{status
= {started,PS}, subscribed = []}); {'EXIT',BootPid,Reason} ->
     %do_boot函数进程不正常终止时 erlang:display({"init terminating in do_boot",Reason}), crash("init terminating in do_boot", [Reason]); {'EXIT',Pid,Reason} ->
    %kernel进程不正常终止时 Kernel = State#state.kernel, terminate(Pid,Kernel,Reason), %% If Pid is a Kernel pid, halt()! boot_loop(BootPid,State); {stop,Reason} ->
    %init:stop 和init:reboot会发送stop消息 stop(Reason,State); {From,fetch_loaded} -> %% Fetch and reset initially loaded modules. From ! {init,State#state.loaded}, garb_boot_loop(BootPid,State#state{loaded = []}); {From,{ensure_loaded,Module}} -> {Res, Loaded} = ensure_loaded(Module, State#state.loaded), From ! {init,Res}, boot_loop(BootPid,State#state{loaded = Loaded}); Msg -> boot_loop(BootPid,handle_msg(Msg,State)) end. %%%%%%%%%%%%%%%%%%%%%%%%%%%% %以下是状态转换后的loop循环 %%%%%%%%%%%%%%%%%%%%%%%%%%%% loop(State) -> receive {'EXIT',Pid,Reason} -> Kernel = State#state.kernel, terminate(Pid,Kernel,Reason), %% If Pid is a Kernel pid, halt()! loop(State); {stop,Reason} -> stop(Reason,State); {From,fetch_loaded} -> %% The Loaded info is cleared in Loaded = State#state.loaded, %% boot_loop but is handled here From ! {init,Loaded}, %% anyway. loop(State); {From, {ensure_loaded, _}} -> From ! {init, not_allowed}, loop(State); Msg ->
    %handle_msg主要处理的就是init模块所提供的一些获取arguments,status之类环境上下文的接口API了 loop(handle_msg(Msg,State)) end. handle_msg(Msg,State0) -> case catch do_handle_msg(Msg,State0) of {new_state,State} -> State; _ -> State0 end. do_handle_msg(Msg,State) -> #state{flags = Flags, status = Status, script_id = Sid, args = Args, subscribed = Subscribed} = State, case Msg of {From,get_plain_arguments} -> From ! {init,Args}; {From,get_arguments} -> From ! {init,get_arguments(Flags)}; {From,{get_argument,Arg}} -> From ! {init,get_argument(Arg,Flags)}; {From,get_status} -> From ! {init,Status}; {From,script_id} -> From ! {init,Sid}; {From,{make_permanent,Boot,Config}} -> {Res,State1} = make_permanent(Boot,Config,Flags,State), From ! {init,Res}, {new_state,State1}; {From,{notify_when_started,Pid}} -> case Status of {InS,PS} when InS =:= started ; PS =:= started -> From ! {init,started}; _ -> From ! {init,ok}, {new_state,State#state{subscribed = [Pid|Subscribed]}} end; X -> case whereis(user) of undefined -> catch error_logger ! {info, self(), {self(), X, []}}; User -> User ! X, ok end end.

以上其实基本已经把boot的init模块部分大略说了一篇,有一些细节还不是很清晰,以后补充

 

 

 

 posted on 2012-12-28 11:50  文武双全大星星  阅读(542)  评论(0编辑  收藏  举报