从一个基本调用开始
{function, call, 0, 2}.
{label,1}.
{line,[{location,"src/examples/test/test.erl",9}]}.
{func_info,{atom,test},{atom,call},0}.
{label,2}.
{move,{atom,c},{x,0}}.
{line,[{location,"src/examples/test/test.erl",10}]}.
{call_ext_only,1,{extfunc,a,b,1}}.
call_ext_only
指令位于beam_hot.h文件,在编译时使用ops.tab文件生成
接下来看下指令代码
OpCase(i_call_ext_last_eQ):
{
E = ADD_BYTE_OFFSET(E, I[2]);;
do {
BeamInstr dis_next;
Export *ep;
ep = (Export*)(I[1]);
DTRACE_GLOBAL_CALL_FROM_EXPORT(c_p, ep);
SET_I(ep->dispatch.addresses[erts_active_code_ix()]);
CHECK_ARGS(I);
dis_next = *I;
if (ERTS_UNLIKELY(FCALLS <= 0)) {
if (ERTS_PROC_GET_SAVED_CALLS_BUF(c_p) && FCALLS > neg_o_reds) {
save_calls(c_p, ep);
} else {
goto context_switch;
}
}
FCALLS--;
Goto(dis_next);
} while (0);ASSERT(!"Fell through 'i_call_ext_last' (-no_next)");
}
Export
导出结构和erts_active_code_ix
激活代码地址索引是我们想要了解的
两个代码相关的核心模块
erts_code_purger.erl
清理接口主要检测是否有进程正在占用当前代码
清理主要分两步,prepare和abort/complete
硬清理和软清理区别在于是否终止正在使用此代码的进程
code.erl
- 加载文件(模块) code_server:load_file/3
- 加载模块服务管理 code_server:try_load_module_2/6
- 加载模块接口 erlang:load_module/2
两个核心函数
erlang:prepare_loading/2
- 包装函数 erts_internal_prepare_loading_2
- 主函数 erts_prepare_loading
- 读取(解码)字节码 beamfile_read
- 加载字节码 load_code
加载阶段主要就是检查字节码有效性并加载到内存中
erlang:finish_loading/1
- 检查权限/是否正在加载/是否被清理过等 finish_loading_1
- 加载模块 finish_loading_1
代码
beam_make_current_old
是把当前数据移动到旧数据
代码
export_list
正是导出函数功能
代码
trampoline
是切换暂存地址到激活地址
同一个模块有两份数据,一份正在(激活)使用,一份旧(副本)数据。如果旧数据正在被进程占用,
soft_purge
不能清理旧代码,也就说无法进行热更新新的代码