`
LeslieWei
  • 浏览: 14106 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

Erlang虚拟机源码阅读笔录(一)虚拟机的启动

阅读更多

去年在北京一个互联网公司实习,当时项目组需要使用和erlang相关的东西,然后给我一个任务,和另外两个同事一起阅读erlang虚拟机的源码,然后给老大写一份报告,我主要阅读的是启动,指令,进程创建以及调度这块的代码。阅读笔记是早就写好了,却一直没有同步到博客上,本来自己资质尚浅,笔记里面也有诸多错误;再则这份阅读笔录也不是完全原创,也借鉴了诸多大牛和官网上的东西。到现在突然想将其开放到博客上,也主要是觉得目前erlang虚拟机相关的资料太少,而现在云计算技术的兴起,却使得这门语言备受青睐,所以编程需求量是蛮大的。由于自己资质尚浅,笔录中的诸多错误,也希望大牛们多多指正,在此感谢你们的宝贵意见。


erl实际上是一个shell脚本,如图1.1:



 
 
图1.1
其先设置了erlang虚拟机执行所需的一系列环境变量,然后调用可执行文件erlexec。erlexec的入口点在 otp_src_R15B01/erts/etc/common/erlexec.c 文件中。erlexec的main函数首先分析erl传入的参数和环境变量,获取CPU的相关信息,选择正确版本的beam可执行文件,然后将传入的参数整理好,加入一些默认参数,最后通过系统调用execv运行beam的emulator,如图1.2:



 
 
图1.2
因此,erl和erlexec都是加载器,最终执行的Erlang虚拟机进程是名字为beam系列的进程。
Beam进程的入口函数在otp_src_R15B01/erts/emulator/sys/unix/erl_main.c中,main结构十分简单,如图1.3:



 
 
图1.3
erl_start函数是真正的erlang emulator的入口函数,到现在才真正进入到erlang虚拟机的世界。erl_start函数位于 otp_src_R15B01/erts/emulator/beam/erl_init.c 文件,Erlang虚拟机初始化相关的代码基本上都在这个文件中。这个函数大约600行,但是结构简单,大部分代码都是在处理参数。把erl_start的主干理出来,如图1.4:



 
 
图1.4
early_init()函数进行一些非常底层的初始化工作,一些具体的数据结构初始化在调度一节再详细说明。erl_init()处理一些和Erlang虚拟机本身的初始化操作,例如各种数据结构的初始化,重点的有对process的初始化,schedulers的初始化,以及schedulers和cpu的bind关系。在R14中由init_shared_memory()函数执行的功能在R15版本中统一收并在erl_int()函数中,这部门工作主要是对虚拟机的内存垃圾回收机制的初始化处理,对应的函数是erts_init_gc()。
load_preloaded()函数将需要预加载的Erlang模块加载至虚拟机。需要预加载的模块都在 otp_src_R15B01/erts/preloaded/ebin 目录下。由于在build Erlang/OTP的时候,本地应该还没有Erlang编译器,所以这个目录下提供的都是编译好的.beam文件。这些模块的源码位于otp_src_R15B01/erts/preloaded/src 目录。预加载模块在build的时候由工具程序 make_preload 生成C语言文件硬编码在虚拟机中了。如果想要修改预加载的文件,例如在里面加上 erlang:display() 表达式打印调试信息,可以修改src中的文件,然后通过编译器erlc生成.beam文件保存在 otp_src_R15B01/erts/preloaded/ebin目录下覆盖原来的文件,再build即可。
在预加载的文件夹中可以看到,预加载的有以下模块:
erl_prim_loader:主要加载器,负责所有模块的加载
erlang:对虚拟机提供的一些BIF的接口
init:init进程的代码
otp_ring0:Erlang虚拟机中第一个进程的代码,启动init
prim_file:文件操作接口
prim_inet:网络操作接口
prim_zip:压缩文件操作接口
zlib:zlib库
load_preloaded()函数是如何将预加载模块加载到虚拟机的呢?首先看该函数的定义,如图1.5:



 
 
图1.5
该函数首先调用sys_preloaded()函数,sys_preloaded函数的定义如图1.6:



 
 
图1.6
该函数只是简单的返回了一个全局的指针变量pre_loaded,该变量在 otp_src_R15B01/erts/emulator/sys/unix/sys.c中被申明为extern Preload pre_loaded[];
Preload是一个结构体类型,定义如下:
typedef struct preload {
    char *name; /* Name of module */
    int  size; /* Size of code */
    unsigned char* code; /* Code pointer */
} Preload;
这里面有个unsigned char*类型的指针,指向了要加载的可执行代码的内存起始地址,再回到上面所说的所有的预加载的*.beam字节码模块都会被make_preload脚本程序硬编码成可直接执行的二进制编码并存储在一个C的unsigned char*类型的数组中,数组的命名为preloaded_+文件名,如图1.7所示:



 
 
图1.7
在对erlang虚拟机源码进行make编译的时候,make_preload脚本程序被解释执行,make_preload程序将所有的字节码文件硬编码成C代码并存储在preload.c文件中(我的ubuntu系统下该文件的路径为:${erl_root}/otp_src_R15B02/erts/emulator/x86_64-unknown-linux-gnu/preload.c),在这个文件中定义并用二进制码初始化相应的“preloaded_+beam文件名”命名的数组,紧接着make_preload脚本将在生成的preload.c文件中定义一个Preload类型的变量pre_load[],并将其成员变量code指向前面定义的preloaded_+beam文件名数组首地址,如图1.8所示:



 
 
图1.8
     初始化完成的pre_load[]数组如图1.9所示:



 
 
图1.9
     在sys.c文件中通过extern申明,将pre_loaded[]变量引入到erlang虚拟机中,至此,预加载过程结束。
把这些必要模块都加载至虚拟机之后,通过erl_first_process_otp()函数创建了Erlang虚拟机上的第一个进程,调用 otp_ring0 模块中的start/2函数。start/2 函数运行init模块的 boot/1 函数,之后开始Erlang/OTP系统的引导过程。这里先把虚拟机的启动过程分析完再讲述Erlang/OTP的引导过程。
创建了第一个进程之后,进程还不能运行,因为还没有创建调度器。erts_start_schedulers()根据CPU的核心数和用户通过参数设置的数值启动某个数目的调度器线程。每一个调度器都在一个线程中运行。调度器挑选要执行的进程,然后执行进程,当进程的reds用完或进程等待IO挂起的时候再挑选另一个进程执行。后面会详细分析Erlang调度器的工作原理。运行了erts_start_schedulers()函数之后Erlang虚拟机才真正运转起来。
启动调度器之后,调用erts_sys_main_thread()函数,也就是说beam进程的主线程进入了erts_sys_main_thread()函数。下面简单分析一下erts_sys_main_thread()函数。如图1.9:



 
 
图1.9
这个函数很简单,屏蔽浮点数异常、通知信号处理线程已经完成了初始化,然后进入一个死循环等待信号。这个select调用表示永远等待文件IO操作,但是什么文件也不等,只是把线程挂起。但是这个函数在收到信号的时候会返回。这里顺便提一下Erlang虚拟机中的信号处理。在之前初始化的时候,设置了信号处理函数,也就是通过函数 init_break_handler() 设置了一些信号的处理函数。这些信号处理函数收到了信号之后实际上将信号通过管道转发给了一个专门处理信号的线程,之前在调用 early_init() 的时候创建了这个线程,这个信号处理线程运行的函数是 signal_dispatcher_thread_func(),这个函数是一个死循环,等待从管道中读取值。虚拟机的主线程通过 smp_sig_notify() 函数将通知消息写入管道发给信号处理线程。
从Erlang虚拟机处理信号的方式可以看出,这种处理方式也是Erlang提倡的进程间通信方式。
下面分析otp_ring0的start/2调用init的boot/1引导Erlang/OTP系统的过程。
init进程的引导过程
init:boot/1的代码如图1.10:



 
 
图1.10
     第2行将当前进程注册为init,于是我们就有了init进程。第4行启动了一个新的进程ON_LOAD_HANDLER,这个进程处理一些和加载相关的事件。然后对传入的参数做一些处理,Start是erl -s参数传入的要运行的MFA列表,Flags0是调用erl传入的一些标志,Args是erl -extra 传入的一些额外参数。接下来这些参数传入boot/3。下面(图1.11)是boot/3的代码:



 
 
图1.11
boot/3调用do_boot/2,设置State,然后就进入boot_loop/2循环。下面是do_boot/2的代码(图1.12):



 
 
图1.12
do_boot/2创建了一个负责引导过程的进程(do_boot/3,没有register,让我们称为do_boot),boot/2最后进入了一个boot_loop循环,接受来自do_boot进程的消息。现在系统上有3个进程,如图1.13所示:



 
 
图1.13
此时的<0.0.0>在boot_loop循环等待接受<0.2.0>发出的和boot相关的消息,<0.1.0>在等待接收和加载相关的消息。下面看do_boot/3的引导过程。第7-10行从传入的参数中获得加载器相关的参数,然后在第11行调用函数start_prim_loader通过erl_prim_loader模块的start/3函数创建了加载器进程。第13-14行从启动脚本中获得启动指令列表。有关启动脚本的格式参见文档 erl -man script ,启动脚本描述了Erlang运行时系统启动的过程,包含了启动过程要执行的一系列指令。如果启动erl的时候没有带-boot Name参数,那么默认使用start.boot启动脚本。start.boot是由start.script生成的。start.script内容摘要如图1.14所示:



 
 
图1.14
do_boot/3中的BootFile就是这个文件,BootList就是从第3行开始的这个列表。列表中的每一项表示一个动作,这些动作包括preLoaded、progress、path、primLoad、kernelProcess和apply,这些动作在erl -man script文档中有详细的解释。do_boot/3的第23行调用eval_script/8函数负责执行这个列表中的每一个动作。图1.15是eval_script/8的代码节选:



 
 
图1.15
eval_script/8对BootList中的每一个动作进行处理。有一些动作要给init进程发送消息,init进程的boot_loop/2循环接收这些消息。boot_loop/2接收的消息中有两个,如图1.16所示:



 
 
图1.16
BootList最后一条指令是{progress,started},对应了boot_loop/2第3行的消息,在执行完这一条指令之后,eval_script/8结束了执行,因此do_boot/3在结束eval_script/8之后调用start_em/1之后就正常退出了,进程<0.2.0>正常退出,boot_loop/2收到'EXIT'消息,init进程进入loop/1循环。此时,init作为初始化的任务已经完成。这一个默认的启动脚本启动了两个应用程序,kernel和STDLIB,前者是一个普通应用程序,后者只是一个库应用程序。如果erl没有传入-noshell参数,kernel还会启动shell和用户交互。这两个应用程序是Erlang最简系统的基础,前者提供了必要的系统服务,例如文件服务、网络服务和错误日志记录服务等,后者提供了编写程序需要使用的各种工具、数据结构以及OTP相关的重要模块。

 

  • 大小: 211.2 KB
  • 大小: 4.1 KB
  • 大小: 13.4 KB
  • 大小: 85.8 KB
  • 大小: 172.8 KB
  • 大小: 21.7 KB
  • 大小: 259.7 KB
  • 大小: 101.7 KB
  • 大小: 8 KB
  • 大小: 11.8 KB
  • 大小: 7.2 KB
  • 大小: 4.3 KB
  • 大小: 13.6 KB
  • 大小: 33.9 KB
  • 大小: 30.2 KB
  • 大小: 10.9 KB
  • 大小: 37.2 KB
分享到:
评论
3 楼 LeslieWei 2013-04-23  
fanchangyong 写道
楼主能把图片整一下吗,看不到图啊。。。

刚才弄了下,现在应该能看到了吧?
2 楼 LeslieWei 2013-04-23  
fanchangyong 写道
楼主能把图片整一下吗,看不到图啊。。。

这个是iteye网站的问题,我发表文章那几天都可以有图的,现在图片全没了,我把word版的传上来,如果有需要,就下载文档看吧。
1 楼 fanchangyong 2013-04-21  
楼主能把图片整一下吗,看不到图啊。。。

相关推荐

    Erlang虚拟机内存管理

    Erlang核心开发者Lukas Larsson在2014年3月份Erlang Factory上的一个演讲详细介绍了Erlang内存体系的原理以及调优案例 根据siyao zheng博客上听写的资源进行的翻译,大致只翻译了80%但核心部分已经完整,希望对大家...

    hex, Erlang虚拟机的软件包管理器.zip

    hex, Erlang虚拟机的软件包管理器 十六进制 Hex是Erlang虚拟机的软件包管理器。这个项目目前提供了与混合。tcm 工具构建的任务。有关安装说明和其他文档,请参阅 hex.pm 。在本地安装十六进制以进行开发: mix ...

    erlang 部分源码

    Erlang是一种面向并发的、动态类型的编程语言,主要用于构建高可用性、容错性和分布式系统。这个压缩包包含的是Erlang的部分源代码,尽管可能不完整,但仍然能够提供对Erlang语言实现机制的深入了解。 1. **Erlang...

    基于Erlang VM的语言

    Erlang虚拟机(Erlang VM,也称为BEAM虚拟机)是Erlang编程语言的核心组成部分,它为Erlang提供了强大的并发特性和故障容错能力。基于Erlang VM的语言充分利用了这些优势,同时也引入了不同语法和编程范式的创新。 ...

    erlang 程序设计 源码

    Erlang是一种面向并发的、动态类型的编程语言,主要用于构建高度可扩展的、容错性强的分布式系统。...在`code`这个文件中,你可以找到实际的源码,通过阅读和分析,将理论知识与实践相结合,提升对Erlang编程的掌握。

    英雄远征erlang源码

    【标题】:“英雄远征erlang源码”指的是一个基于Erlang编程语言开发的服务器端源代码,用于支持网络游戏“英雄远征”的运行。Erlang是一种并发性极强、容错性好的函数式编程语言,常用于构建高可用性的分布式系统,...

    英雄远征erlang服务器源码含数据库

    7. **学习路径**:初学者可以通过阅读和调试这些源码,了解Erlang的基本语法、模块结构、进程通信以及数据库操作。同时,也可以研究其错误处理和日志记录机制,以增强对Erlang实际开发流程的理解。 8. **实践经验**...

    erlang21.0源码

    Erlang是一种面向并发的、动态类型的编程语言,主要用于构建分布式、容错性强的系统。OTP(Open Telecom ...同时,对于研究分布式系统、并发编程或者虚拟机设计的人来说,Erlang 21.0的源码也是一个宝贵的教育资源。

    Erlang编程指南

    Erlang是运行于虚拟机的解释性语言,但是现在也包含有乌普萨拉大学高性能Erlang计划(HiPE)开发的本地代码编译器,自R11B-4版本开始,Erlang也开始支持脚本式解释器。在编程范型上,Erlang属于多重范型编程语言,...

    erlang 聊天室源码

    在本项目中,我们看到"erlang 聊天室源码"是一个初学者级别的实践项目,开发者花费了两周时间完成,并且在设计上受到了知名消息中间件rabbitMQ的启发。 首先,我们要理解Erlang的核心特性,这包括轻量级进程...

    英雄远征源码[erlang]

    《英雄远征》是一款基于Erlang编程语言开发的网络游戏,其源码的公开为开发者提供了深入了解游戏服务端架构和Erlang在大型分布式系统应用的机会。Erlang是一种为并发、容错和实时系统设计的函数式编程语言,以其在高...

    erlang服务端源码

    erlang 服务端代码 例子,演示了如何管理角色

    erlang聊天室源码

    本篇将深入探讨一个基于Erlang实现的简单聊天室源码,帮助初学者理解Erlang在构建实时通信系统中的应用。 首先,`聊天室需求文件.doc`提供了项目的基本需求,通常会包含聊天室的功能描述、用户交互方式、性能指标等...

    erlang源码包

    Erlang是一种面向并发、分布式计算的编程语言,由瑞典电信设备制造商Ericsson为了实现分布式实时高可靠性系统而开发。其源码包适用于Linux环境,这使得开发者可以在Linux操作系统上编译并运行Erlang系统。在提供的...

    erlang server源码

    Erlang Server源码分析与详解 Erlang是一种面向并发的、函数式编程语言,以其在分布式系统、高可用性和容错性方面的优势而受到广泛关注。尤其在构建大规模聊天室服务器这样的实时通信系统中,Erlang的性能表现突出...

    Erlang程序设计及源码

    本资源包含了一本Erlang程序设计的入门经典书籍及其配套源码,适合初学者深入理解Erlang编程。 1. **Erlang简介** Erlang以其强大的并发处理能力而闻名,它采用了轻量级进程模型,使得在单个系统中可以同时运行...

    远古封神Server(erlang源码)+文档+mongodb数据库

    本文将深入探讨"远古封神Server"项目,这是一个采用Erlang编程语言构建的游戏服务器,搭配MongoDB数据库来实现高效的数据存储和处理。Erlang以其并发能力、容错性和分布式特性在实时系统和大规模并发应用中得到了...

    《Erlang程序设计》源码

    《Erlang程序设计》是一本深入探讨Erlang编程语言的书籍,其源码包含在提供的压缩包中。Erlang是一种并发性极强、适用于构建分布式系统和实时软实时系统的函数式编程语言。该书通过源码实例,帮助读者理解和掌握...

    erlang入门级练习:LeetCode OJ问题的部分erlang 源码

    我自己在新学erlang,在LeetCode OJ上找了题目练习,题目很适合新手熟悉语言,但是LeetCode OJ里面只有几门主流语言的答案,下面是已完成的erlang源代码,后续有空再做其他问题续传,题目包含:(源码开头都有题目...

Global site tag (gtag.js) - Google Analytics