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 的元文件. 我们知道 ...
随机推荐
- 实现Client Credentials Grant
[OAuth]基于DotNetOpenAuth实现Client Credentials Grant Client Credentials Grant是指直接由Client向Authorizatio ...
- c# in deep 之Lambda表达式
从很多方面,Lambda表达式都可以看作是C# 2的匿名方法的一种演变.匿名方法能做的几乎一切事情都可以用Lambda表达式来完成,而且其更简洁.易读.下面是一个简单例子. class Film ...
- springmvc国际化 基于浏览器语言的国际化配置
当前标签: springmvc springmvc国际化 基于浏览器语言的国际化配置 苏若年 2013-10-09 13:03 阅读:305 评论:0 SpringMVC中应用Ajax异步通讯 ...
- Nginx 配置基于域名的虚拟
编辑配置文件 vi /etc/nginx/nginx.conf user www www; worker_processes 2; error_log logs/error.log not ...
- HTML5 拖拽效果实现
<!DOCTYPE html> <html> <head lang="en"> <meta charset="UTF-8&quo ...
- 通过DialogFragment从DatePicker或TimePicker中获取日期数据
通过DialogFragment从DatePicker或TimePicker中获取日期数据 一个activity类,里面存有date和time的变量,想通过dialogfragment的方式获取用户输 ...
- HashTable类模板_C++
好久没看数据结构了,今天终于要用到hash,整理一下写了个hash类模板 template<typename T> class DataType { public: T key; Data ...
- 四.redis 事务
redis对事务的支持目前还比较简单.redis只能保证一个client发起的事务中的命令可以连续的执行,而中间不会插入其他client的命令. 由于redis是单线程来处理所有client的请求的所 ...
- SSH整合创建SessionFactory
在spring中的 applicationContext.xml中配置如下信息 <!-- 配置数据连接类 --> <bean id="dataSource" cl ...
- breakpad是Google开源的一套跨平台工具
windows下捕获dump之Google breakpad_client的理解 breakpad是Google开源的一套跨平台工具,用于dump的处理.很全的一套东西,我这里只简单涉及break ...