erlang虚拟机代码执行原理
erlang代码编译过程
1. erlang核心代码
2. erlang汇编码
3. erlang BEAM
4. erlang运行时代码
erlang代码从编译到执行过程
|
File
|
Path
|
|
beam_makeops
|
erts/emulator/utils/
|
|
ops.tab
|
erts/emulator/beam/
|
|
beam_opcodes.c
|
erts/emulator/<machine>/opt/smp/
|
|
beam_load.c
|
erts/emulator/beam/
|
|
genop.tab
|
lib/compiler/src/
|
erlang 虚拟机执行代码的原理
erlang虚拟机概述
erlang进程
进程的栈和堆
堆用于存储复杂的数据结构,如元组,列表或大整数。栈被用来存储简单的数据,还有指向堆中复杂数据的数据指针。栈有指针指向堆,但不会有指针从堆到栈。
寄存器
- /*
- * X register zero; also called r(0)
- */
- register Eterm x0 REG_x0 = NIL;
register修饰符的作用是暗示编译器,某个变量将被频繁使用,尽可能将其保存在CPU的寄存器中,以加快其存储速度。随着编译程序设计技术的进步,在决定那些变量应该被存到寄存器中时,现在的编译器能比程序员做出更好的决定,往往会忽略register修饰符。但是就erlang虚拟机对寄存器变量的使用程度,应该是可以利用到CPU寄存器的好处。
指令调度
- while(1){
- opcode = *vPC++;
- switch(opcode){
- case i_call_fun:
- ..
- break;
- case call_bif_e:
- ..
- break;
- //and many more..
- }
- };
字节码在虚拟机中执行,执行过程类似CPU执行指令过程,分为取指,解码,执行3个过程。通常情况下,每个操作码对应一段处理函数,然后通过一个无限循环加一个switch的方式进行分派。
- start()->
- spawn(fun() -> fun1(1) end). %% 创建进程,执行 fun1/1
- fun1(A) ->
- A1 = A + 1,
- B = trunc(A1), %% 执行 trunc/1
- {ok, A1+B}.
以上,进程在执行函数( trunc/1)调用前,会将当前的本地变量和返回地址指针CP写入栈。然后,在执行完这个函数(trunc/1)后再从栈取出CP指令和本地变量,根据CP指针返回调用处,继续执行后面的代码。
Threaded Code(线索化代码)
拓展阅读
BIF(内建函数)
- Export* bif_export[BIF_SIZE];
- BifEntry bif_table[] = {
- {am_erlang, am_abs, 1, abs_1, abs_1},
- {am_erlang, am_adler32, 1, adler32_1, wrap_adler32_1},
- {am_erlang, am_adler32, 2, adler32_2, wrap_adler32_2},
- {am_erlang, am_adler32_combine, 3, adler32_combine_3, wrap_adler32_combine_3},
- {am_erlang, am_apply, 3, apply_3, wrap_apply_3},
- {am_erlang, am_atom_to_list, 1, atom_to_list_1, wrap_atom_to_list_1},
- typedef struct bif_entry {
- Eterm module;
- Eterm name;
- int arity;
- BifFunction f; // bif函数
- BifFunction traced; // 函数调用跟踪函数
- } BifEntry;
erlang BEAM模拟器启动时会初始化bif函数表,
- init_emulator:
- {
- em_call_error_handler = OpCode(call_error_handler);
- em_apply_bif = OpCode(apply_bif);
- beam_apply[0] = (BeamInstr) OpCode(i_apply);
- beam_apply[1] = (BeamInstr) OpCode(normal_exit);
- beam_exit[0] = (BeamInstr) OpCode(error_action_code);
- beam_continue_exit[0] = (BeamInstr) OpCode(continue_exit);
- beam_return_to_trace[0] = (BeamInstr) OpCode(i_return_to_trace);
- beam_return_trace[0] = (BeamInstr) OpCode(return_trace);
- beam_exception_trace[0] = (BeamInstr) OpCode(return_trace); /* UGLY */
- beam_return_time_trace[0] = (BeamInstr) OpCode(i_return_time_trace);
- /*
- * Enter all BIFs into the export table.
- */
- for (i = 0; i < BIF_SIZE; i++) {
- ep = erts_export_put(bif_table[i].module, //模块名
- bif_table[i].name,
- bif_table[i].arity);
- bif_export[i] = ep;
- ep->code[3] = (BeamInstr) OpCode(apply_bif);
- ep->code[4] = (BeamInstr) bif_table[i].f; // BIF函数
- /* XXX: set func info for bifs */
- ep->fake_op_func_info_for_hipe[0] = (BeamInstr) BeamOp(op_i_func_info_IaaI);
- }
下面写个简单的例子说明,
- /*
- * 以下截取 bif 处理过程
- */
- OpCase(call_bif_e):
- {
- Eterm (*bf)(Process*, Eterm*, BeamInstr*) = GET_BIF_ADDRESS(Arg(0)); // 根据参数获取bif实际执行函数
- Eterm result;
- BeamInstr *next;
- PRE_BIF_SWAPOUT(c_p);
- c_p->fcalls = FCALLS - 1;
- if (FCALLS <= 0) {
- save_calls(c_p, (Export *) Arg(0));
- }
- PreFetch(1, next);
- ASSERT(!ERTS_PROC_IS_EXITING(c_p));
- reg[0] = r(0);
- result = (*bf)(c_p, reg, I); // 执行bif函数
- ASSERT(!ERTS_PROC_IS_EXITING(c_p) || is_non_value(result));
- ERTS_VERIFY_UNUSED_TEMP_ALLOC(c_p);
- ERTS_HOLE_CHECK(c_p);
- ERTS_SMP_REQ_PROC_MAIN_LOCK(c_p);
- PROCESS_MAIN_CHK_LOCKS(c_p);
- if (c_p->mbuf || MSO(c_p).overhead >= BIN_VHEAP_SZ(c_p)) {
- Uint arity = ((Export *)Arg(0))->code[2];
- result = erts_gc_after_bif_call(c_p, result, reg, arity);
- E = c_p->stop;
- }
- HTOP = HEAP_TOP(c_p);
- FCALLS = c_p->fcalls;
- if (is_value(result)) {
- r(0) = result;
- CHECK_TERM(r(0));
- NextPF(1, next);
- } else if (c_p->freason == TRAP) {
- SET_CP(c_p, I+2);
- SET_I(c_p->i);
- SWAPIN;
- r(0) = reg[0];
- Dispatch();
- }
上面涉及到一个宏,就是取得bif函数地址。
- #define GET_BIF_ADDRESS(p) ((BifFunction) (((Export *) p)->code[4]))
根据前面提到的,((Export *) p)->code[4] 就是 bif_table表的中BIF函数的地址。
扩展指令集
| Type | Description |
|---|---|
| t | An arbitrary term, e.g. {ok,[]} |
| I | An integer literal, e.g. 137 |
| x | A register, e.g. R1 |
| y | A stack slot |
| c | An immediate term, i.e. atom/small int/nil |
| a | An atom, e.g. 'ok' |
| f | A code label |
| s | Either a literal, a register or a stack slot |
| d | Either a register or a stack slot |
| r | A register R0 |
| P | A unsigned integer literal |
| j | An optional code label |
| e | A reference to an export table entry |
| l | A floating-point register |
erlang虚拟机代码执行原理的更多相关文章
- erlang虚拟机代码运行原理
erlang是开源的,非常多人都研究过源码.可是.从erlang代码到c代码.这是个不小的跨度.并且代码也比較复杂. 所以这里,我利用一些时间,整理下erlang代码的运行过程.从erlang代码编译 ...
- 2020/1/28 PHP代码审计之代码执行漏洞
0x00代码执行原理 应用程序在调用一些能够将字符串转换为代码的函数(如PHP中的eval)时,没有考虑用户是否控制这个字符串,将造成代码执行漏洞. 该漏洞主要存在于eval().assert().p ...
- 从虚拟机指令执行的角度分析JAVA中多态的实现原理
从虚拟机指令执行的角度分析JAVA中多态的实现原理 前几天突然被一个"家伙"问了几个问题,其中一个是:JAVA中的多态的实现原理是什么? 我一想,这肯定不是从语法的角度来阐释多态吧 ...
- DEDECMS数据库执行原理、CMS代码层SQL注入防御思路
我们在上一篇文章中学习了DEDECMS的模板标签.模板解析原理,以及通过对模板核心类的Hook Patch来对模板的解析流量的攻击模式检测,达到修复模板类代码执行漏洞的目的 http://www.cn ...
- Java虚拟机JVM内存分区及代码执行机制
详见:http://blog.yemou.net/article/query/info/tytfjhfascvhzxcyt230 1. JVM体系结构 图1 JVM体系结构 方法区:存放JVM ...
- Erlang虚拟机的启动
Erlang虚拟机的启动 erl实际上是一个shell脚本,设置几个环境变量之后,调用执行erlexec.erlexec的入口点在 otp_src_R15B01/erts/etc/common/erl ...
- Profile 分析 Erlang 虚拟机源码时要注意的一个问题
最近用 Intel Vtune 剖析 Erlang 虚拟机的运行,想看看那些函数和语句耗时最多,遇到一个小问题,那就是 Vtune 给出的源码和汇编码对应有问题.这个问题在 profile 或 deb ...
- Python程序的执行原理(转载)
Python程序的执行原理 2013-09-17 10:35 佚名 tech.uc 1. 过程概述 Python先把代码(.py文件)编译成字节码,交给字节码虚拟机,然后虚拟机一条一条执行字节码指令 ...
- java-程序执行原理
java应用可以打包成jar 格式,jar格式其实只是一种很普通的压缩格式,与zip格式一样,只不过是它会在压缩文件的目录结构中增加一个META-INF/ MANIFEST.MF 的元文件. 我们知道 ...
随机推荐
- 昨天面试新浪 java试题
昨天去了新浪网面试,感觉新浪真的挺不错的,工作环境那叫一个好啊.对于一般屌丝的话进到这种公司就可以呆一辈子了.做了面试之后感觉不管大公司还是小公司都还是注重基础和你平时工作的积累的.所以不能抱怨现 ...
- Unknown
鉴于自己的直觉总是很准,所以这次再相信一次好了 T1:我觉得极有可能考到的是 1.对于栈,队列的模拟: 2.数论(不是像gcd那样的题目,而是加法原理乘法原理斥容,或是极具数学推导的东西,当然有可能用 ...
- 8个免费实用的C++GUI库
8个免费实用的C++GUI库 C++标准中并没有包含GUI,这也使得C++开发图形化界面需要依赖于第三方的库.实际上,图形界面恰恰是C++的强项,小到平常使用的各类桌面软件,大到魔兽世界这样的游戏,都 ...
- ShardedJedis实现学习
ShardedJedis实现学习-我们到底能走多远系列(33) 我们到底能走多远系列(31) 扯淡: 工作是容易的赚钱是困难的 恋爱是容易的成家是困难的 相爱是容易的相处是困难的 决定是容易的可是等待 ...
- 开发win8 metro monogame,显示pubcenter广告时会使游戏卡住的问题的解决方法。
开发win8 metro游戏,使用monogame,并且还加上pubcenter广告,但是在x64的机子上遇到了问题,广告一出,游戏卡死.经过一系列的判断,发现monogame一直在update和dr ...
- Newlife商业源码分享
[商业源码]生日大放送-Newlife商业源码分享 今天是农历六月二十三,是@大石头的生日,记得每年生日都会有很劲爆的重量级源码送出,今天Newlife群和论坛又一次疯狂了,吃水不忘挖井人,好的东西肯 ...
- ExtJS初接触 —— 了解 Ext Core
ExtJS初接触 —— 了解 Ext Core Ext Core是一款和jQuery媲美的轻型JS库,基于MIT许可.对于Dom的操作,我个人还是比较喜欢用jQuery.当然如果项目中用的是ExtJS ...
- python alembic which comes from SQLalchemy
alembic it's tutorial: http://alembic.readthedocs.org/en/latest/tutorial.html
- Hadoop 统计文件中某个单词出现的次数
如文件word.txt内容如下: what is you name? my name is zhang san. 要求统计word.txt中出现“is”的次数? 代码如下: PerWordMapper ...
- 关于.NET异常处理的思考(上)
年关将至,对于大部分程序员来说,马上就可以闲下来一段时间了,然而在这个闲暇的时间里,唯有争论哪门语言更好可以消磨时光,估计最近会有很多关于java与.net的博文出现,我表示要作为一个吃瓜群众,静 ...