美文网首页erlang
erlang 启动顺序

erlang 启动顺序

作者: Alking | 来源:发表于2019-12-10 00:25 被阅读0次

erlang 启动顺序

本文解读erlang的启动顺序,VM在启动的时候到底做了什么?

1.启动入口

调用堆栈如下:

init:boot/1(Pid<0,0,0>)

otp_ring0:start/2(系统入口)

2.使用-init_debug可以将boot script信息打印出来

$ erl -init_debug
{progress,preloaded}
{progress,kernel_load_completed}
{progress,modules_loaded}
{start,heart}
{start,logger}
{start,application_controller}
{progress,init_kernel_started}
{apply,......}
{progress,applications_loaded}
{apply,{application,start_boot,[kernel,permanent]}}
Erlang/OTP 21 [erts-10.3.4] [source] [64-bit] [smp:4:4] [ds:4:4:10] [async-threads:1] [hipe]

{apply,{application,start_boot,[stdlib,permanent]}}
{apply,{c,erlangrc,[]}}
{progress,started}
Eshell V10.3.4  (abort with ^G)
1> 

3.代码解读(init.erl)

% init.erl

boot(BootArgs)->
...
...
do_boot(Init, Flags, Start)->
...
  % 获取 -boot 参数,默认 "/bin/start"
  BootFile = bootfile(Flags,Root),
  % 读取 BootFile.boot 文件,文件格式是 {script,Id,CmdList}
  % Id: Otp版本,我本机是"21”
  % CmdList: 启动参数
  % {preLoaded...}
  % {progress...}
  % {path...}
  % {kernelProcess....}
  % {apply,{M,F,A}},
  BootList = get_boot(BootFile,Root),
  ......
  eval_script(BootList, Es)

4.$ROOT/bin/start.boot脚本内容

1> {ok,[Root]}=init:get_argument(root).
{ok,[["/usr/lib/erlang"]]}
2> BootFile = lists:flatten(Root ++ "/bin/start.boot").
"/usr/lib/erlang/bin/start.boot"
3> {ok,Bin}=file:read_file(BootFile).
{ok,<<131,104,3,100,0,6,115,99,114,105,112,116,104,2,107,
      0,10,69,114,108,97,110,103,47,79,84,80,...>>}
4> Term = erlang:binary_to_term(Bin).
{script,{"Erlang/OTP","21"},
        [{preLoaded,[atomics,counters,erl_prim_loader,erl_tracer...]},
         {kernel_load_completed},
         {progress,kernel_load_completed},
         {path,["$ROOT/lib/kernel-6.3.1/ebin"]},
         {primLoad,[error_handler,application...]},
         {path,["$ROOT/lib/stdlib-3.8.1/ebin"]},
         {primLoad,...},
        % kernel,stdlib模块载入完成
         {progress,modules_loaded},
         {path,["$ROOT/lib/kernel-6.3.1/ebin",
                "$ROOT/lib/stdlib-3.8.1/ebin"]},
         % 开启 heart
         {kernelProcess,heart,{heart,start,[]}}, 
         % 开启 logger_server
         {kernelProcess,logger,{logger_server,start_link,[]}},
         % 开启 application_controller
         {kernelProcess,application_controller...},
         {progress,init_kernel_started},
         % 载入 application: stdlib,kernel
         {apply,{application,load,[{application,stdlib,[...]}]}},
         {progress,applications_loaded},
         % 启动 application kernel
         {apply,{application,start_boot,[kernel,permanent]}},
         % 启动 application stdlib
         {apply,{application,start_boot,[stdlib|...]}},
         % 运行 $HOME/.erlang文件,这边可以参考我的另外一篇文章erlang中的特殊文件
         {apply,{c,erlangrc,[]}},
         {progress,started}]}
5>

从上文可以看到模块的加载顺序是

  1. erts 中的模块,如atomics,counters...
  2. stdlib 中的模块,如error_handler,gen_server...
  3. kernel 中的模块,如application_starter...
  4. 其他自定义的application中的模块,由于模块不像其他语言有命名空间,所以自己起模块名得注意了

5. erlang 是如何加载系统

erlang按照boot文件中的cmd list顺序执行

eval_script([{progress, Info} = Progress | T], #es{debug = Deb} = Es) ->
  % progress 只是打印进度,别的什么都不干
  debug(Deb, Progress),
  init ! {self(), progress, Info},
  eval_script(T, Es);
  
eval_script([{preLoaded, _} | T], #es{} = Es) ->
  % preLoaded 什么都不干
  eval_script(T, Es);
  
eval_script([{path, Path} | T], #es{path = false, pa = Pa, pz = Pz,
  % 添加PATH
  path_choice = PathChoice,
  vars = Vars} = Es) ->
  RealPath0 = make_path(Pa, Pz, Path, Vars),
  RealPath = patch_path(RealPath0, PathChoice),
  erl_prim_loader:set_path(RealPath),
  eval_script(T, Es);
  
eval_script([{path, _} | T], #es{} = Es) ->
  %% Ignore, use the command line -path flag.
  eval_script(T, Es);
  
eval_script([{kernel_load_completed} | T], #es{load_mode = Mode} = Es0) ->
  Es = case Mode of
         embedded -> Es0;
         _ -> Es0#es{prim_load = false}
       end,
  eval_script(T, Es);
eval_script([{primLoad, Mods} | T], 
#es{init = Init, prim_load = PrimLoad} = Es) when is_list(Mods) ->
  % primLoad 载入需要的模块
  case PrimLoad of
    true ->
      load_modules(Mods, Init);
    false ->
      %% Do not load now, code_server does that dynamically!
      ok
  end,
  eval_script(T, Es);
eval_script([{kernelProcess, Server, {Mod, Fun, Args}} | T],
    #es{init = Init, debug = Deb} = Es) ->
  % kernelProcess 开启服务,如heart,logger, application_controller
  debug(Deb, {start, Server}),
  start_in_kernel(Server, Mod, Fun, Args, Init),
  eval_script(T, Es);
eval_script([{apply, {Mod, Fun, Args}} = Apply | T], #es{debug = Deb} = Es) ->
  % 执行脚本MFA,如启动 kernel,stdlib,c:erlnagc()
  debug(Deb, Apply),
  apply(Mod, Fun, Args),
  eval_script(T, Es);
eval_script([], #es{}) ->
  ok;
eval_script(What, #es{}) ->
  % 不能识别的CMD,报错直接退出
  exit({'unexpected command in bootfile', What}).

总结

了解并掌握erlang的启动顺序,加深对VM的了解,并且对于自己实现boot file有很大的帮助。

相关文章

网友评论

    本文标题:erlang 启动顺序

    本文链接:https://www.haomeiwen.com/subject/atkwgctx.html