hibernate组只有一个实现,即hibernate/3,可是在搞明白proc_lib的hibernate实现细节以前,须要先弄清楚erlang:hibernate/3的运行机制。shell
erlang:hibernate/3会使当前进程当即陷入到waiting状态,并即刻进行垃圾回收,只有当进程接收到消息的时候才会从waiting状态恢复,并从指定的回调函数开始运行,以前的进行栈信息,hibernate会所有丢弃。写个测试代码:express
test_hibernate() -> Pid = spawn( fun() -> receive Msg -> io:format("Before hibernate, I received msg: ~p~n", [Msg]) end, try erlang:hibernate(?MODULE, a, []) catch Type:Reason -> io:format("Catch exception: ~p:~p~n", [Type, Reason]) end end ), io:format("before hibernate, heap size: ~p~n", [process_info(Pid, total_heap_size)]), Pid ! "hi", timer:sleep(1000), io:format("in hibernate, heap size: ~p~n", [process_info(Pid, total_heap_size)]), Pid ! 1, timer:sleep(1000), io:format("after weakup, heap size: ~p~n", [process_info(Pid, total_heap_size)]), Pid ! 0, timer:sleep(1000), io:format("after exception happend, heap size: ~p~n", [process_info(Pid, total_heap_size)]). a() -> receive Msg -> io:format("I received in hibernate callback ~p~n", [Msg]), 1 / Msg, a() end .
输出:app
13> c(test_link). test_link.erl:49: Warning: the result of the expression is ignored (suppress the warning by assigning the expression to the _ variable) {ok,test_link} 14> test_link:test_hibernate(). before hibernate, heap size: {total_heap_size,233} Before hibernate, I received msg: "hi" in hibernate, heap size: {total_heap_size,1} I received in hibernate callback 1 after weakup, heap size: {total_heap_size,233} I received in hibernate callback 0 =ERROR REPORT==== 12-Dec-2017::16:20:06 === Error in process <0.84.0> with exit value: {badarith,[{test_link,a,0,[{file,"test_link.erl"},{line,49}]}]} after exception happend, heap size: undefined ok
能够看到在进程执行hibernate以前,占据的堆空间为233个字,可是在hibernate以后,只占了1个字,当有消息进入进程邮箱时,进程会解除hibernate的状态,占据的堆空间大小会从新恢复,但由于丢弃了以前的栈信息,异常捕获不会起做用(而这也是proc_lib:hibernate/3所要解决的问题)。函数
能够想象,当咱们有大量进程长期处于waiting状态,须要等待某个时间点被唤醒时,经过hibernate节省的内存开销是至关可观的,但hibernate也有个问题,就是栈信息会被丢弃,前面咱们已经看到,proc_lib都是经过exit_p来统一处理错误的,但若是在目标函数里面调用了erlang:hibernate,那么异常就不会再经过exit_p来处理,形成失控,所以这就是proc_lib也封装了一个hibernate函数的缘由。测试
hibernate(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> erlang:hibernate(?MODULE, wake_up, [M, F, A]).
重点是wake_up函数的实现:编码
wake_up(M, F, A) when is_atom(M), is_atom(F), is_list(A) -> try apply(M, F, A) catch Class:Reason -> exit_p(Class, Reason, erlang:get_stacktrace()) end.
再这里面,咱们又从新经过try catch以及exit_p来处理了一下异常。这样就能确保hibernate回调的函数也能符合"OTP设计原则"。atom
前面咱们说start函数时讲过,spawn新进程后,当前进程会等待新进程返回的信息,其中一种就是ack,用于通知当前监控进程本身工做完成,init_ack就是封装了ack消息的发送,实现很是的简单:spa
init_ack(Parent, Return) -> Parent ! {ack, self(), Return}, ok. init_ack(Return) -> [Parent|_] = get('$ancestors'), init_ack(Parent, Return).
咱们前面在讲述spawn和start组的时候已经讲述了init_p的做用,再也不赘述。但proc_lib是把init_p做为API公开出来的,咱们本身能够在须要的场景去包装它。hibernate
format组的函数用于将CrashReport格式化成字符串,能够选择不一样的编码以及栈深度,略。设计
stop组提供了两个函数,stop/1和stop/3:
stop(Process) -> stop(Process, normal, infinity). stop(Process, Reason, Timeout) -> {Pid, Mref} = erlang:spawn_monitor(do_stop(Process, Reason)), receive {'DOWN', Mref, _, _, Reason} -> ok; {'DOWN', Mref, _, _, {noproc,{sys,terminate,_}}} -> exit(noproc); {'DOWN', Mref, _, _, CrashReason} -> exit(CrashReason) after Timeout -> exit(Pid, kill), receive {'DOWN', Mref, _, _, _} -> exit(timeout) end end. do_stop(Process, Reason) -> fun() -> Mref = erlang:monitor(process, Process), ok = sys:terminate(Process, Reason, infinity), receive {'DOWN', Mref, _, _, ExitReason} -> exit(ExitReason) end end.
看上去挺绕的,咱们先在shell中测试一下stop的行为:
15> 15> Pid = spawn(fun() -> receive Msg -> io:format("I received msg: ~p~n", [Msg]) end end). <0.86.0> 16> proc_lib:stop(Pid). I received msg: {system,{<0.88.0>,#Ref<0.0.3.69>},{terminate,normal}} ** exception exit: {normal,{sys,terminate,[<0.86.0>,normal,infinity]}} in function proc_lib:stop/3 (proc_lib.erl, line 796)
经过stop/1,目标进程收到了terminate而后抛出一个exit异常。
18> proc_lib:stop(Pid, hehe, 10). ** exception exit: noproc in function proc_lib:stop/3 (proc_lib.erl, line 794)
Pid已经不存在了,抛出一个noproc异常。
17> Pid2 = spawn(fun() -> receive Msg -> io:format("I received msg: ~p~n", [Msg]) end end). <0.91.0> 19> proc_lib:stop(Pid2, hehe, 10). I received msg: {system,{<0.96.0>,#Ref<0.0.5.355>},{terminate,hehe}} ** exception exit: {normal,{sys,terminate,[<0.91.0>,hehe,infinity]}} in function proc_lib:stop/3 (proc_lib.erl, line 796)
再看下超时:
25> Pid5 = spawn(fun() -> receive Msg -> io:format("I received msg: ~p~n", [Msg]), timer:sleep(100000), io:format("finis hed ~n~p") end end). <0.113.0> 26> 26> proc_lib:stop(Pid5, hehe, 5000). I received msg: {system,{<0.115.0>,#Ref<0.0.5.404>},{terminate,hehe}} ** exception exit: timeout in function proc_lib:stop/3 (proc_lib.erl, line 801)
进程收到了消息,5秒钟后会抛出一个timeout异常,同时目标进程Pid5也会被干掉,后面的"finished"没有打印出来。
总之,stop组的函数对进程提供了一种更好的终结方式,能够更灵活的定义终结消息,固然目标进程也要接收约定,同时也提供了超时机制,让进程在没法响应外界请求时强制kill掉。
嗯,以上就是proc_lib模块的所有内容了。