ERLANG OTP源码分析 – sys
sys模块主要有两大用途.统计跟踪目标gen进程,代码热升级,尤其后者非常重要,下面从erlang源码层面来讲述这两大工作原理.
代码热升级
代码热升级能做到gen进程不停(自然其中的socket,file都不需要关闭咯,100%的在线率多有趣的功能阿)的情况下自动载入新的代码, 这点对于其它程序语言来说是件非常难的事情,而对于erlang确只是简单几行代码就能解决问题.
演示代码
这里有演示代码,你可以自行运行.
热升级的原理和步骤
1.首先gen进程已经启动并处于正常工作状态处理业务逻辑,现在需要对代码进行升级,修改原来的内部State状态.
2.修改代码,编译出新的beam文件.
compile:file(Mod).
注意不能使用c(Mod). c命令实际包含一个编译和加载和清理老代码作用,这里还不能加载哦, 老的内存状态运行在新的代码肯定报错.
3.利用sys:suspend函数使得gen进程陷入一种挂起状态.
这种挂起状态的gen进程仅仅会处理system和’EXIT’两类的消息,而不会处理业务逻辑.
sys.erl ==== suspend_loop(SysState, Parent, Mod, Debug, Misc, Hib) -> case Hib of true -> suspend_loop_hib(SysState, Parent, Mod, Debug, Misc, Hib); _ -> receive {system, From, Msg} -> handle_system_msg(SysState, Msg, From, Parent, Mod, Debug, Misc, Hib); {\\\'EXIT\\\', Parent, Reason} -> Mod:system_terminate(Reason, Parent, Debug, Misc) end end.
此时发送过来的业务消息都被存在mailbox中,而代码不会走到业务模块.
4.因为业务不会运行了,所以清理老代码,载入新代码,
code:purge(Mod). code:load_file(Mod).
好了这个时候已经新的代码了.而gen进程里的内存状态还是老状态(State).
5. 调用change_code做一个内存状态的转化.
sys:change_code(Name, Mod, OldVsn, Extra).
change_code属于system消息,告诉gen进程去调用Mod里的code_change函数,针对的vsn是OldVsn,额外的参数是Extra.于是gen进程最终就调用Mod里的code_change函数,内存State在此时进行了转化.
6.回到业务逻辑
sys:resume(swap_test).
好了代码,内存里的信息都是正确的了,我们就开启业务,resume最终会让gen进程跑到system_continue这个函数
system_continue(Parent, Debug, [Name, StateName, StateData, Mod, Time]) -> loop(Parent, Name, StateName, StateData, Mod, Time, Debug).
于是gen进程又走回到正常的业务逻辑loop函数去,于是积累的消息和新的消息就开始正常的处理了.
release_handler_1.erl底层也是调用sys模块进行代码热升级的,所以立即sys的工作原理很重要.
统计跟踪
开启统计
sys:statistics(Name, true).
实际上是开始记录了一个初始值
init_stat() -> {erlang:localtime(), process_info(self(), reductions), 0, 0}.
现在使用sys:statistics(Name,get)就可以获得进程的运行时间和处理过的消息数目了.
3> sys:statistics(swap_test, get). {ok,[{start_time,{{2012,7,31},{19,58,25}}}, {current_time,{{2012,7,31},{19,58,32}}}, {reductions,18}, {messages_in,0}, {messages_out,0}]}
关闭统计
sys:statistics(Name, false).
获取进程和进程内字典的信息
5> sys:get_status(swap_test). {status,, {module,gen_server}, [[{\\\'$ancestors\\\',[]}, {\\\'$initial_call\\\',{swap_test,init,1}}], running,,[], [{header,\"Status for generic server swap_test\"}, {data,[{\"Status\",running}, {\"Parent\",}, {\"Logged events\",[]}]}, {data,[{\"State\",{state,1,2}}]}]]}
最终调用的是sys.get_status和gen进程里的format_status/2函数
get_status(SysState, Parent, Mod, Debug, Misc) -> {status, self(), {module, Mod}, [get(), SysState, Parent, Debug, Misc]}.
跟踪进程
sys:trace(code_lock, true).
实际上使得gen进程在处理每一条消息的时候多调用一次print_event函数打印出这条信息和前后状态.
Debug1 = sys:handle_debug(Debug, {?MODULE, print_event}, Name, {in, Msg}),
看看激活trace后的效果
4> swap_test:test_call(). *DBG* swap_test got call counter from call counter *DBG* swap_test sent 1 to , new state {state,3,2}
以上就是sys模块的重要功能.
建议继续学习:
- Nginx源码分析-事件循环 (阅读:5000)
- Hive的入口 -- Hive源码解析 (阅读:4848)
- Storm源码浅析之topology的提交 (阅读:4482)
- Hive源码解析-之-语法解析器 (阅读:4345)
- Nginx源码分析-内存池 (阅读:4225)
- Nginx源码分析-Epoll模块 (阅读:4001)
- Lua GC 的源码剖析 (2) (阅读:3967)
- Lua GC 的源码剖析 (4) (阅读:3502)
- Redis的事件循环与定时器模型 (阅读:3168)
- ExtJS源码研究笔记之总评 (阅读:3134)
扫一扫订阅我的微信号:IT技术博客大学习
- 作者:hoterran 来源: 运维和开发
- 标签: 源码
- 发布时间:2012-08-03 00:02:57
- [69] Twitter/微博客的学习摘要
- [67] IOS安全–浅谈关于IOS加固的几种方法
- [65] android 开发入门
- [65] 如何拿下简短的域名
- [63] find命令的一点注意事项
- [62] Go Reflect 性能
- [61] 流程管理与用户研究
- [60] Oracle MTS模式下 进程地址与会话信
- [59] 图书馆的世界纪录
- [57] 读书笔记-壹百度:百度十年千倍的29条法则