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模塊的所有內容了。