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模块的重要功能.
