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 的元文件. 我们知道 ...
随机推荐
- Working with Entity Relations in OData
Working with Entity Relations in OData 前言 阅读本文之前,您也可以到Asp.Net Web API 2 系列导航进行查看 http://www.cnblogs. ...
- iOS基础 - 内存分析
一.内存的重要性 程序是否可用的最根本的制约因素. 十分影响用户体验. 程序的crash有很多情况都是内存的原因. 二.iOS平台内存警告机制 iOS系统不支持虚拟内存,一旦出现内存不足的情况,iOS ...
- 浅谈DevExpress<三>:在GridView中加载动态图片
今天的演示效果如下:在GridView中的下拉框中选中一种颜色,则后面的加载相应的图片,如下图: 1.
- 4 MySQL与PHP连接
目录: 1. 连接概述2. 创建php文件进行MySQL连接3. 查看连接效果 1. 连接概述 上文讲述了LAMP开发模型,并且使用AppServ进行安装.这时候就要体现优势了.本节将介绍在直接使用P ...
- 用NPOI实现导入导出csv、xls、xlsx数据功能
用NPOI实现导入导出csv.xls.xlsx数据功能 直接上代码 首先定义一个接口 如果需要直接操作文件的话,就自己在封装一次 然后定义csv类的具体实现 这个需要引入命名空间LumenWo ...
- SQL Server中tempdb的management
对<SQL Server中tempdb的management>的一些更正和补充 对<SQL Server中tempdb的management>的一些更正和补充 前几天看了这 ...
- mac 下安装oh my zsh
1.直接从github上下载 git clone git://github.com/robbyrussell/oh-my-zsh.git ~/.oh-my-zsh 2.拷贝到账户目录下 cp ~/. ...
- Oracle中忘记sys和System密码的办法
打开cmd:sqlplus/nolog connect/as sysdba; alter user sys identified by ***; alter user system identifie ...
- CSS border-radius 圆角
本文转自:http://www.kwstu.com/Admin/ViewArticle/201409151549476225 border-radius属性主要用于设置div圆角效果. 使用方法 bo ...
- easyui datagrid自定义操作列
通过formatter方法给Jquery easyui 的datagrid 每行增加操作链接 我们都知道Jquery的EasyUI的datagrid可以添加并且自定义Toolbar, 这样我们选择一行 ...