日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

Erlang虚拟机的启动

發(fā)布時(shí)間:2025/3/14 编程问答 55 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Erlang虚拟机的启动 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

Erlang虛擬機(jī)的啟動(dòng)

erl實(shí)際上是一個(gè)shell腳本,設(shè)置幾個(gè)環(huán)境變量之后,調(diào)用執(zhí)行erlexec。erlexec的入口點(diǎn)在 otp_src_R15B01/erts/etc/common/erlexec.c 文件。erlexec的main函數(shù)首先分析erl傳入的參數(shù)和環(huán)境變量,選擇正確版本的beam可執(zhí)行文件,然后將傳入的參數(shù)整理好,加入一些默認(rèn)參數(shù),最后通過系統(tǒng)調(diào)用execv運(yùn)行beam虛擬機(jī)。例如在smp環(huán)境中,運(yùn)行的就是 beam.smp 版本的虛擬機(jī)。因此,erl和erlexec都是加載器,最終執(zhí)行的Erlang虛擬機(jī)進(jìn)程是名字為beam系列的進(jìn)程。

beam進(jìn)程的入口點(diǎn)在?otp_src_R15B01/erts/emulator/sys/unix/erl_main.c 。在這個(gè)文件中,main函數(shù)只有一行:

  • erl_start(argc,?argv);?
  • 通過erl_start這個(gè)函數(shù)真正進(jìn)入Erlang虛擬機(jī)的世界了。erl_start函數(shù)位于?otp_src_R15B01/erts/emulator/beam/erl_init.c 文件,Erlang虛擬機(jī)初始化相關(guān)的代碼基本上都在這個(gè)文件中。這個(gè)函數(shù)大約600行,但是結(jié)構(gòu)簡(jiǎn)單,大部分代碼都是在處理參數(shù)。把erl_start的主干理出來(lái),就是這樣的:

  • void?erl_start(int?argc,?char?**argv)?
  • ?{?
  • ?????early_init(&argc,?argv);?
  • ?????處理各種參數(shù)?
  • ?????設(shè)置信號(hào)處理函數(shù)?
  • ?????erl_init(ncpu);?
  • ?????init_shared_memory(boot_argc,?boot_argv);?
  • ?????load_preloaded();?
  • ?????erts_initialized?=?1;?
  • ?????erl_first_process_otp("otp_ring0",?NULL,?0,?boot_argc,?boot_argv);?
  • ?????erts_start_schedulers();?
  • ?????erts_sys_main_thread();??
  • ?}?
  • early_init()函數(shù)進(jìn)行一些非常底層的初始化工作。erl_init()處理一些和Erlang虛擬機(jī)本身的初始化操作,例如各種數(shù)據(jù)結(jié)構(gòu)的初始化。init_shared_memory()進(jìn)行一些和內(nèi)存回收相關(guān)的初始化。

    load_preloaded()函數(shù)將需要預(yù)加載的Erlang模塊加載至虛擬機(jī)。需要預(yù)加載的模塊都在?otp_src_R15B01/erts/preloaded/ebin 目錄下。由于在build Erlang/OTP的時(shí)候,本地應(yīng)該還沒有Erlang編譯器,所以這個(gè)目錄下提供的都是編譯好的.beam文件。這些模塊的源碼位于otp_src_R15B01/erts/preloaded/src 目錄。預(yù)加載模塊在build的時(shí)候由工具程序 make_preload 生成C語(yǔ)言文件硬編碼在虛擬機(jī)中了。如果想要修改預(yù)加載的文件,例如在里面加上 erlang:display() 表達(dá)式打印調(diào)試信息,可以修改src中的文件,然后通過編譯器erlc生成.beam文件保存在?otp_src_R15B01/erts/preloaded/ebin目錄下覆蓋原來(lái)的文件,再build即可。

    在預(yù)加載的文件夾中可以看到,預(yù)加載的有以下模塊:

    erl_prim_loader:主要加載器,負(fù)責(zé)所有模塊的加載

    erlang:對(duì)虛擬機(jī)提供的一些BIF的接口

    init:init進(jìn)程的代碼

    otp_ring0:Erlang虛擬機(jī)中第一個(gè)進(jìn)程的代碼,啟動(dòng)init

    prim_file:文件操作接口

    prim_inet:網(wǎng)絡(luò)操作接口

    prim_zip:壓縮文件操作接口

    zlib:zlib庫(kù)

    把這些必要模塊都加載至虛擬機(jī)之后,通過erl_first_process_otp()函數(shù)創(chuàng)建了Erlang虛擬機(jī)上的第一個(gè)進(jìn)程,調(diào)用?otp_ring0 模塊中的start/2函數(shù)。start/2 函數(shù)運(yùn)行init模塊的 boot/1 函數(shù),之后開始Erlang/OTP系統(tǒng)的引導(dǎo)過程。這里先把虛擬機(jī)的啟動(dòng)過程分析完再講述Erlang/OTP的引導(dǎo)過程。

    創(chuàng)建了第一個(gè)進(jìn)程之后,進(jìn)程還不能運(yùn)行,因?yàn)檫€沒有創(chuàng)建調(diào)度器。erts_start_schedulers()根據(jù)CPU的核心數(shù)和用戶通過參數(shù)設(shè)置的數(shù)值啟動(dòng)某個(gè)數(shù)目的調(diào)度器線程。每一個(gè)調(diào)度器都在一個(gè)線程中運(yùn)行。調(diào)度器挑選要執(zhí)行的進(jìn)程,然后執(zhí)行進(jìn)程,當(dāng)進(jìn)程的reds用完或進(jìn)程等待IO掛起的時(shí)候再挑選另一個(gè)進(jìn)程執(zhí)行。以后再撰文詳細(xì)分析Erlang調(diào)度器的工作原理。運(yùn)行了erts_start_schedulers()函數(shù)之后Erlang虛擬機(jī)才真正運(yùn)轉(zhuǎn)起來(lái)。

    啟動(dòng)調(diào)度器之后,調(diào)用erts_sys_main_thread()函數(shù),也就是說beam進(jìn)程的主線程進(jìn)入了erts_sys_main_thread()函數(shù)。下面簡(jiǎn)單分析一下erts_sys_main_thread()函數(shù)。

  • void?erts_sys_main_thread(void)?
  • ?{?
  • ?????erts_thread_disable_fpe();?
  • ?????smp_sig_notify(0);?/*?Notify?initialized?*/?
  • ?????while?(1)?{?
  • ?????????/*?Wait?for?a?signal?to?arrive...?*/?
  • ?????????select(0,?NULL,?NULL,?NULL,?NULL);?
  • ?????}?
  • ?}?
  • 這個(gè)函數(shù)很簡(jiǎn)單,屏蔽浮點(diǎn)數(shù)異常、通知信號(hào)處理線程已經(jīng)完成了初始化,然后進(jìn)入一個(gè)死循環(huán)等待信號(hào)。這個(gè)select調(diào)用表示永遠(yuǎn)等待文件IO操作,但是什么文件也不等,只是把線程掛起。但是這個(gè)函數(shù)在收到信號(hào)的時(shí)候會(huì)返回。這里順便提一下Erlang虛擬機(jī)中的信號(hào)處理。在之前初始化的時(shí)候,設(shè)置了信號(hào)處理函數(shù),也就是通過函數(shù)?init_break_handler() 設(shè)置了一些信號(hào)的處理函數(shù)。這些信號(hào)處理函數(shù)收到了信號(hào)之后實(shí)際上將信號(hào)通過管道轉(zhuǎn)發(fā)給了一個(gè)專門處理信號(hào)的線程,之前在調(diào)用 early_init() 的時(shí)候創(chuàng)建了這個(gè)線程,這個(gè)信號(hào)處理線程運(yùn)行的函數(shù)是?signal_dispatcher_thread_func(),這個(gè)函數(shù)是一個(gè)死循環(huán),等待從管道中讀取值。虛擬機(jī)的主線程通過?smp_sig_notify() 函數(shù)將通知消息寫入管道發(fā)給信號(hào)處理線程。

    從Erlang虛擬機(jī)處理信號(hào)的方式可以看出,這種處理方式也是Erlang提倡的進(jìn)程間通信方式。

    下面分析otp_ring0的start/2調(diào)用init的boot/1引導(dǎo)Erlang/OTP系統(tǒng)的過程。

    init進(jìn)程的引導(dǎo)過程

    init:boot/1的代碼如下:

  • boot(BootArgs)?->?
  • ?????register(init,?self()),?
  • ?????process_flag(trap_exit,?true),?
  • ?????start_on_load_handler_process(),?
  • ?????{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).?
  • 第2行將當(dāng)前進(jìn)程注冊(cè)為init,于是我們就有了init進(jìn)程。第4行啟動(dòng)了一個(gè)新的進(jìn)程ON_LOAD_HANDLER,這個(gè)進(jìn)程處理一些和加載相關(guān)的事件。然后對(duì)傳入的參數(shù)做一些處理,Start是erl -s參數(shù)傳入的要運(yùn)行的MFA列表,Flags0是調(diào)用erl傳入的一些標(biāo)志,Args是erl -extra 傳入的一些額外參數(shù)。接下來(lái)這些參數(shù)傳入boot/3。下面是boot/3的代碼:

  • boot(Start,Flags,Args)?->?
  • ?????BootPid?=?do_boot(Flags,Start),?
  • ?????State?=?#state{flags?=?Flags,?
  • ????????????args?=?Args,?
  • ????????????start?=?Start,?
  • ????????????bootpid?=?BootPid},?
  • ?????boot_loop(BootPid,State).?
  • boot/3調(diào)用do_boot/2,設(shè)置State,然后就進(jìn)入boot_loop/2循環(huán)。下面是do_boot/2的代碼:

  • 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)),?
  • ?????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/2創(chuàng)建了一個(gè)負(fù)責(zé)引導(dǎo)過程的進(jìn)程(do_boot/3,沒有register,讓我們稱為do_boot),boot/2最后進(jìn)入了一個(gè)boot_loop循環(huán),接受來(lái)自do_boot進(jìn)程的消息?,F(xiàn)在系統(tǒng)上有3個(gè)進(jìn)程,如下圖所示:

    此時(shí)的<0.0.0>在boot_loop循環(huán)等待接受<0.2.0>發(fā)出的和boot相關(guān)的消息,<0.1.0>在等待接收和加載相關(guān)的消息。下面看do_boot/3的引導(dǎo)過程。第7-10行從傳入的參數(shù)中獲得加載器相關(guān)的參數(shù),然后在第11行調(diào)用函數(shù)start_prim_loader通過erl_prim_loader模塊的start/3函數(shù)創(chuàng)建了加載器進(jìn)程。第13-14行從啟動(dòng)腳本中獲得啟動(dòng)指令列表。有關(guān)啟動(dòng)腳本的格式參見文檔?erl -man script ,啟動(dòng)腳本描述了Erlang運(yùn)行時(shí)系統(tǒng)啟動(dòng)的過程,包含了啟動(dòng)過程要執(zhí)行的一系列指令。如果啟動(dòng)erl的時(shí)候沒有帶-boot Name參數(shù),那么默認(rèn)使用start.boot啟動(dòng)腳本。start.boot是由start.script生成的。start.script內(nèi)容摘要如下所示:

  • {script,?
  • ?????{"OTP??APN?181?01","R15B01"},?
  • ?????[{preLoaded,?
  • ??????????[erl_prim_loader,erlang,init,otp_ring0,prim_file,prim_inet,prim_zip,zlib]},?
  • ??????{progress,preloaded},?
  • ??????{path,["$ROOT/lib/kernel/ebin","$ROOT/lib/stdlib/ebin"]},?
  • ??????{primLoad,[error_handler]},?
  • ??????{kernel_load_completed},?
  • ??????{progress,kernel_load_completed},?
  • ??????{path,["$ROOT/lib/kernel/ebin"]},?
  • ??????{primLoad,?
  • ??????????[application,application_controller,application_master,?
  • ???????????...?
  • ???????????standard_error,user,user_drv,user_sup,wrap_log_reader]},?
  • ??????{path,["$ROOT/lib/stdlib/ebin"]},?
  • ??????{primLoad,?
  • ??????????[array,base64,beam_lib,binary,c,calendar,dets,dets_server,dets_sup,?
  • ???????????...?
  • ???????????supervisor_bridge,sys,timer,unicode,win32reg,zip]},?
  • ??????{progress,modules_loaded},?
  • ??????{path,["$ROOT/lib/kernel/ebin","$ROOT/lib/stdlib/ebin"]},?
  • ??????{kernelProcess,heart,{heart,start,[]}},?
  • ??????{kernelProcess,error_logger,{error_logger,start_link,[]}},?
  • ??????{kernelProcess,application_controller,?
  • ??????????{application_controller,start,?
  • ??????????????[{application,kernel,?
  • ???????????????????...}]}},?
  • ??????{progress,init_kernel_started},?
  • ??????{apply,?
  • ??????????{application,load,?
  • ??????????????[{application,stdlib,?
  • ???????????????????...}]}},?
  • ??????{progress,applications_loaded},?
  • ??????{apply,{application,start_boot,[kernel,permanent]}},?
  • ??????{apply,{application,start_boot,[stdlib,permanent]}},?
  • ??????{apply,{c,erlangrc,[]}},?
  • ??????{progress,started}]}.?
  • do_boot/3中的BootFile就是這個(gè)文件,BootList就是從第3行開始的這個(gè)列表。列表中的每一項(xiàng)表示一個(gè)動(dòng)作,這些動(dòng)作包括preLoaded、progress、path、primLoad、kernelProcess和apply,這些動(dòng)作在erl -man script文檔中有詳細(xì)的解釋。do_boot/3的第23行調(diào)用eval_script/8函數(shù)負(fù)責(zé)執(zhí)行這個(gè)列表中的每一個(gè)動(dòng)作。下面是eval_script/8的代碼節(jié)選:

  • eval_script([{progress,Info}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)?->?
  • ?????debug(Deb,{progress,Info}),?
  • ?????init?!?{self(),progress,Info},?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);?
  • ?eval_script([{preLoaded,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)?->?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);?
  • ?eval_script([{path,Path}|CfgL],Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice)?->?
  • ?????...?
  • ?????eval_script(CfgL,Init,{Pa,Pz},Vars,false,Ph,Deb,PathChoice);?
  • ?eval_script([{path,_}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)?->?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);?
  • ?eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,embedded,Par},Deb,PathChoice)?->?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,{true,embedded,Par},Deb,PathChoice);?
  • ?eval_script([{kernel_load_completed}|CfgL],Init,PathFs,Vars,P,{_,E,Par},Deb,PathChoice)?->?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);?
  • ?eval_script([{primLoad,Mods}|CfgL],Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice)?
  • ?????...?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,{true,E,Par},Deb,PathChoice);?
  • ?eval_script([{primLoad,_Mods}|CfgL],Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice)?->?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,{false,E,Par},Deb,PathChoice);?
  • ?eval_script([{kernelProcess,Server,{Mod,Fun,Args}}|CfgL],Init,?
  • ?????????????PathFs,Vars,P,Ph,Deb,PathChoice)?->?
  • ?????start_in_kernel(Server,Mod,Fun,Args,Init),?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);?
  • ?eval_script([{apply,{Mod,Fun,Args}}|CfgL],Init,PathFs,Vars,P,Ph,Deb,PathChoice)?->?
  • ?????...?
  • ?????eval_script(CfgL,Init,PathFs,Vars,P,Ph,Deb,PathChoice);?
  • ?eval_script([],_,_,_,_,_,_,_)?->?
  • ?????ok;?
  • ?eval_script(What,_,_,_,_,_,_,_)?->?
  • ?????exit({'unexpected?command?in?bootfile',What}).?
  • eval_script/8對(duì)BootList中的每一個(gè)動(dòng)作進(jìn)行處理。有一些動(dòng)作要給init進(jìn)程發(fā)送消息,init進(jìn)程的boot_loop/2循環(huán)接收這些消息。boot_loop/2接收的消息中有以下兩個(gè):

  • boot_loop(BootPid,?State)?->?
  • ?????receive?
  • ?????????{BootPid,progress,started}?->?
  • ?????????????{InS,_}?=?State#state.status,?
  • ?????????????notify(State#state.subscribed),?
  • ?????????????boot_loop(BootPid,State#state{status?=?{InS,started},?
  • ???????????????????????????????????????????subscribed?=?[]});?
  • ?????????......?
  • ?????????{'EXIT',BootPid,normal}?->?
  • ?????????????{_,PS}?=?State#state.status,?
  • ?????????????notify(State#state.subscribed),?
  • ?????????????loop(State#state{status?=?{started,PS},?
  • ??????????????????????????????subscribed?=?[]});?
  • ?????????......?
  • ?????end.?
  • BootList最后一條指令是{progress,started},對(duì)應(yīng)了boot_loop/2第3行的消息,在執(zhí)行完這一條指令之后,eval_script/8結(jié)束了執(zhí)行,因此do_boot/3在結(jié)束eval_script/8之后調(diào)用start_em/1之后就正常退出了,進(jìn)程<0.2.0>正常退出,boot_loop/2收到'EXIT'消息,init進(jìn)程進(jìn)入loop/1循環(huán)。此時(shí),init作為初始化的任務(wù)已經(jīng)完成。這一個(gè)默認(rèn)的啟動(dòng)腳本啟動(dòng)了兩個(gè)應(yīng)用程序,kernel和STDLIB,前者是一個(gè)普通應(yīng)用程序,后者只是一個(gè)庫(kù)應(yīng)用程序。如果erl沒有傳入-noshell參數(shù),kernel還會(huì)啟動(dòng)shell和用戶交互。這兩個(gè)應(yīng)用程序是Erlang最簡(jiǎn)系統(tǒng)的基礎(chǔ),前者提供了必要的系統(tǒng)服務(wù),例如文件服務(wù)、網(wǎng)絡(luò)服務(wù)和錯(cuò)誤日志記錄服務(wù)等,后者提供了編寫程序需要使用的各種工具、數(shù)據(jù)結(jié)構(gòu)以及OTP相關(guān)的重要模塊。

    小結(jié)?

    通過本文的分析可以看出,Erlang虛擬機(jī)很像一個(gè)運(yùn)行了操作系統(tǒng)的計(jì)算機(jī)。erl對(duì)應(yīng)的是BIOS,加載對(duì)應(yīng)bootloader的erlexec。erlexec加載BEAM虛擬機(jī),BEAM虛擬機(jī)對(duì)應(yīng)了操作系統(tǒng)。接下來(lái)BEAM進(jìn)行初步的初始化,初始化執(zhí)行環(huán)境,對(duì)應(yīng)了操作系統(tǒng)的初始化。初始化完成之后,BEAM像Linux一樣加載系統(tǒng)中的第一個(gè)進(jìn)程init。init進(jìn)程讀取啟動(dòng)列表,執(zhí)行啟動(dòng)系統(tǒng)的步驟。執(zhí)行完這些步驟之后,Erlang成為了一個(gè)完全完成了初始化過程可以運(yùn)行的系統(tǒng)。Erlang像操作系統(tǒng)一樣,有自己的調(diào)度系統(tǒng),內(nèi)存管理系統(tǒng),還有和外界交互的I/O系統(tǒng)。只不過內(nèi)存管理系統(tǒng)更加的智能,可以主動(dòng)幫助進(jìn)程進(jìn)行垃圾回收。I/O系統(tǒng)以系統(tǒng)服務(wù)的方式存在,通過Erlang消息通信的方式向其他進(jìn)程提供服務(wù),因此Erlang的進(jìn)程只需要通過消息這一種語(yǔ)義就能和外界交換數(shù)據(jù)。Erlang中的模塊就好像操作系統(tǒng)中的動(dòng)態(tài)共享庫(kù),只要加載到系統(tǒng)中,就可以供所有的進(jìn)程訪問。多個(gè)模塊可以組織為應(yīng)用程序。Erlang的模塊命名是平坦的,因此不同應(yīng)用程序中的模塊不能重名。Erlang的應(yīng)用程序是對(duì)模塊和進(jìn)程的一種組織方式,從一個(gè)應(yīng)用程序可以包含一組進(jìn)程的角度看,Erlang的應(yīng)用程序有點(diǎn)類似于Linux系統(tǒng)中的進(jìn)程。

    原文:http://www.cnblogs.com/zhengsyao/archive/2012/08/15/Erlang-otp_start_up.html

    轉(zhuǎn)載于:https://www.cnblogs.com/yanwei-wang/p/6272294.html

    總結(jié)

    以上是生活随笔為你收集整理的Erlang虚拟机的启动的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。