erlang虚拟机代码执行原理

erlang是开源的,很多人都研究过源代码。但是,从erlang代码到c代码,这是个不小的跨度,而且代码也比较复杂。所以这里,我利用一些时间,整理下erlang代码的执行过程,从erlang代码编译过程,到代码执行过程做讲解,然后重点讲下虚拟机执行代码的原理。将本篇文章,献给所有喜欢erlang的人。

erlang代码编译过程

erlang对开发者是友好的,从erlang程序文件编译成能被erlang虚拟机识别的beam文件,在这个编译过程还对开发者暴露中间代码。借助这个中间代码,我们就可以逐步探究erlang代码的执行过程。



这是erlnag的编译过程,当然,最开始和大多数编译器一样,首先会将程序文件转换成语法树,但这个转换对我们来说阅读的意义不大,所以归结于以上3个过程。

1. erlang核心代码
确切的叫法是Core Erlang,使用了类似Haskell 的语法,每个变量都用“Let” 声明。在erlang shell通过以下方式可以获取模块的Core Erlang代码,将会生成test.core文件
c(test, to_core).
实际上core文件可以直接编译成beam文件,如下:
c(test, from_core).

2. erlang汇编码
这个是erlang代码编译成beam前的汇编代码,虽然在erlang打包成beam,以及加载到VM时会进一步优化,但汇编码实际上可以看成erlang代码到c代码的纽带。但理解汇编码而不是很容易,这里要知道erlang VM的设计基于寄存器,其中有两类重要的寄存器,传递参数的x寄存器,和在函数内用作本地变量的y寄存器。在erlang shell通过以下方式可以获取模块的汇编代码,将会生成test.S文件
c(test, to_asm). 或是 c(test, 'S').
当然,S文件也支持编译成beam文件,如下:
c(test, from_asm).


3. erlang BEAM
beam文件是不可阅读的,只是给VM识别,内容包括了代码,原子,导入导出函数,属性,编译信息等数据块。

4.  erlang运行时代码
运行时代码是指模块加载到VM后的代码,erlang对开发者暴露了底层的接口。当模块加载后,在erlang shell下通过以下方式可以获取模块的运行时代码,就会生成test.dis文件
erts_debug:df(test).

这里,细心的同学会发现,通过对比erlang汇编码和运行时代码,发现指令代码是不完全相同的。一方面,erlang会对指令进一步做优化;另外,erlang使用了两种指令集,有限指令集和扩展指令集,在beam文件使用了有限指令集,然后在加载到VM时展开为扩展指令集。有论文说是为了减少Beam的大小,这点我没有做过实质性的探究,我只是觉得有限指令集比较短,更容易阅读被人理解。关于有限指令集和扩展指令集的差别,我在文章最后的拓展阅读做了讨论。


erlang代码从编译到执行过程
前面介绍了erlang代码编译的过程,现在再来说明erlang代码从编译到执行的完整过程。文章erlang版本以R16B02作说明。



这里,erlang代码先被编译成beam,然后加载到VM中,最后再被模拟器所识别和调用。
其中,beam文件的加载过程会将beam的字节码形式的数据转成Threaded code和数据。前面也提到,beam文件的字节码数据包含有代码块,这里是将指令展开,转成Threaded code(线索化代码),每条指令包含了opcode(操作码)和operands(操作数),另外还对operands做修正,比如调用外部函数,这里会找到这个外部函数的导出地址,这样每次代码执行的时候就不用再去函数表查找到这个函数,就可以直接执行代码。

Beam的加载逻辑是在 beam_load.c 完成的,指令集的转换在beam_opcodes.c做了映射,而beam_opcodes.c文件是在编译Erlang源码过程有Perl脚本beam_makeops根据ops.tab生成的。所有有限指令集可以在genop.tab找到。



参考:http://blog.csdn.net/zhangxinrun/article/details/50385143

猜你喜欢

转载自rd-030.iteye.com/blog/2339396
今日推荐