Friends familiar with erlang/elixir should know the concept of application. It is a special structure used to start and stop an application. Whenever we create an erlang/elixir project, we also create an application with the same name. When using dependency libraries, generally, each dependency library is also an application, which will be loaded and started when we run the project.
So how does erlang manage these applications? This is: application_ Where the controller works. All application loading, startup and other state saving and changes must go through this process.
# List all current application status > :application_controller.info()
Virtual application
Not all applications will start the process tree. Some applications do not start any process even if they are in the: started state. Let's call this application virtual application.
track
Use bony I mentioned in my previous article_ Trace library to track when load ing and start ing an application:_ What did the controller process do.
stop application
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}
First to code_ The server confirms that some necessary modules for printing log have been loaded, and the results of this step will be cached. There is no need to ask again later. If the application has an application process, a: stop message will be sent to it. Finally, print out the log that the application has stopped.
start and unload do not require too complex message interaction, which is similar to stop.
load application
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}
During load application, AC passes code_ The server obtains the file paths of all applications, and then obtains the exact modules and. app files of the application. Then request the parameters of this application from the: init process. We won't delve into the specific effects of these interactions here. We can interpret them in detail in the following articles: the role of the init process. It is equivalent to the creator in erlang world, with PID < 0.0.0 >.