乐趣区

关于elixir:elixir-0082-application-controller-应用是如何被载入和启动的

相熟 erlang/elixir 的敌人们应该晓得 application 的概念,它是一种非凡的构造,用于启动和进行一个利用。每当咱们新建一个 erlang/elixir 我的项目,也同时新建了一个同名的 利用。在应用依赖库的时候,个别每个依赖库也是一个利用,会在咱们运行我的项目时被载入和启动。

那么,erlang 是如何治理这些利用的呢?这就是 :application_controller 发挥作用的中央了。所有的利用的载入、启动等状态的保留和变更,都要通过这个过程。

# 列出以后的全副利用状态
> :application_controller.info()

虚利用

不是所有的利用都会启动过程树,有些利用即便处于 :started 状态,也没有启动任何过程。权且称这种利用为虚利用吧。

跟踪

利用我之前文章里提到的 bony_trance 库来跟踪一下 在 load 和 start 一个利用时,:application_controller 过程都做了什么吧。

stop 利用

iex(3)> :application_controller.stop_application :play
#PID<0.44.0> RECEIVED                                                 +67.069019s
MESSAGE: {:"$gen_call", {#PID<0.199.0>, #Reference<0.2783639343.3287547907.121340>}, {:stop_application, :play}}
#PID<0.44.0> SENT TO: :code_server                                    +0.000047s
MESSAGE: {:code_call, #PID<0.44.0>, {:ensure_loaded, Logger.Translator}}
#PID<0.44.0> RECEIVED                                                 +0.004582s
MESSAGE: {:code_server, {:module, Logger.Translator}}
#PID<0.44.0> SENT TO: :code_server                                    +0.000033s
MESSAGE: {:code_call, #PID<0.44.0>, {:ensure_loaded, Logger.Utils}}
#PID<0.44.0> RECEIVED                                                 +0.002413s
MESSAGE: {:code_server, {:module, Logger.Utils}}
#PID<0.44.0> SENT TO: :code_server                                    +0.000012s
MESSAGE: {:code_call, #PID<0.44.0>, {:ensure_loaded, :calendar}}
#PID<0.44.0> RECEIVED                                                 +0.012388s
MESSAGE: {:code_server, {:module, :calendar}}

12:38:54.714 [info]  Application play exited: :stopped
:ok
#PID<0.44.0> SENT TO: Logger                                          +0.000015s
MESSAGE: {:notify, {:info, #PID<0.64.0>, {Logger, ["Application", "play", "exited:" | ":stopped"], {{2021, 11, 28}, {12, 38, 54, 714}}, [erl_level: :notice, domain: [:otp], error_logger: %{report_cb: &:application_controller.format_log/1, tag: :info_report, type: :std_info}, file: "application_controller.erl", function: "info_exited/3", gl: #PID<0.64.0>, line: 2119, mfa: {:application_controller, :info_exited, 3}, module: :application_controller, pid: #PID<0.44.0>, report_cb: &:application_controller.format_log/2, time: 1638074334714339]}}}
#PID<0.44.0> SENT TO: #PID<0.199.0>                                   +0.000015s
MESSAGE: {#Reference<0.2783639343.3287547907.121340>, :ok}

首先向 code_server 确认一些必要的用于打印 log 的模块是已载入的, 这一步的后果会缓存,之后不用反复询问。如果该利用有 application 过程,则会向其发送 :stop 音讯。最初打印出利用已进行的 log。

start 及 unload 并不需要太简单的音讯交互,与 stop 相似。

load 利用

iex(9)> :application_controller.load_application :play
#PID<0.44.0> RECEIVED                                                 +26.948851s
MESSAGE: {:"$gen_call", {#PID<0.199.0>, #Reference<0.2783639343.3287547907.121460>}, {:load_application, :play}}
#PID<0.44.0> SENT TO: :code_server                                    +0.000014s
MESSAGE: {:code_call, #PID<0.44.0>, :get_path}
#PID<0.44.0> RECEIVED                                                 +0.000210s
MESSAGE: {:code_server, [...]}
#PID<0.44.0> SENT TO: #PID<0.10.0>                                    +0.000043s
MESSAGE: {#PID<0.44.0>, {:list_dir, '.../play/_build/dev/lib/play/consolidated'}}
#PID<0.44.0> RECEIVED                                                 +0.008352s
MESSAGE: {#PID<0.10.0>, {:ok, ['Elixir.Inspect.beam', 'Elixir.IEx.Info.beam', 'Elixir.String.Chars.beam', 'Elixir.List.Chars.beam', 'Elixir.Collectable.beam', 'Elixir.Enumerable.beam']}}
#PID<0.44.0> SENT TO: #PID<0.10.0>                                    +0.000018s
MESSAGE: {#PID<0.44.0>, {:list_dir, '/Users/linjiezhang/Documents/play/_build/dev/lib/play/ebin'}}
#PID<0.44.0> RECEIVED                                                 +0.010195s
MESSAGE: {#PID<0.10.0>, {:ok, ['Elixir.MyTracer.beam', 'Elixir.Play.beam', 'play.app', 'Elixir.Demo.beam']}}
#PID<0.44.0> SENT TO: #PID<0.10.0>                                    +0.000026s
MESSAGE: {#PID<0.44.0>, {:get_file, '/Users/linjiezhang/Documents/play/_build/dev/lib/play/ebin/play.app'}}
#PID<0.44.0> RECEIVED                                                 +0.000519s
MESSAGE: {#PID<0.10.0>, {:ok, "{application,play,\n             [{applications,[kernel,stdlib,elixir,logger,syntax_tools,\n                             bony_trace]},\n              {description,\"play\"},\n              {modules,['Elixir.Demo','Elixir.MyTracer','Elixir.Play']},\n              {registered,[]},\n              {vsn,\"0.1.0\"}]}.\n", '/Users/linjiezhang/Documents/play/_build/dev/lib/play/ebin/play.app'}}
:ok
#PID<0.44.0> SENT TO: :init                                           +0.000149s
MESSAGE: {#PID<0.44.0>, {:get_argument, :play}}
#PID<0.44.0> RECEIVED                                                 +0.000102s
MESSAGE: {:init, :error}
#PID<0.44.0> SENT TO: #PID<0.199.0>                                   +0.000009s
MESSAGE: {#Reference<0.2783639343.3287547907.121460>, :ok}

在 load 利用时,AC 通过 code_server 获取了全副利用的文件门路,之后拿到了此利用的确切的模块以及 .app 文件。而后向 :init 过程申请此利用的参数。这里就不深究这些交互的具体作用了,之后的文章里能够具体解读一下 :init 过程的作用。它相当于 erlang 世界里的创世主,领有 PID<0.0.0>。

退出移动版