[erlang]proc_lib源码浅析
源碼位置位于安裝目錄的lib/stdlib/src下。
?
之前在使用gen_server時,由于之前自己實現(xiàn)過一個gen_server,因此對它內(nèi)部的機制也能知道個七七八八,最近在用erlang的fsm模塊,突然想讀一讀它得源碼,這才突然發(fā)現(xiàn)erlang的源碼內(nèi)部還是做了很復(fù)雜的工作,尤其是有個“陰魂不散”的模塊proc_lib。
?
無論是gen_server也好,gen_fsm也好,實際上在start的時候,都是調(diào)用的底層gen模塊的start/start_link函數(shù),由start函數(shù)調(diào)用do_spawn函數(shù),在這里,就和我之前想象的不一樣了,在我之前的印象里,這里應(yīng)該直接用spawn,回調(diào)用戶編寫的init函數(shù),但是,實際上不是這樣的,我們看下源碼:
Time = timeout(Options),? ? %這個函數(shù)獲取timeout的時間,默認為infinity
然后開始調(diào)用proc_lib的start函數(shù):
proc_lib:start(?MODULE, init_it, [GenMod, self(), self(), Name, Mod, Args, Options], Time, spawn_opts(Options))
?
其中,spawn_opts實在獲取spawn的參數(shù)。
?
我們跟進到proc_lib的內(nèi)部,看看start函數(shù)做了些什么。
?
在start函數(shù)的第一行返回了一個Pid,那么我們已經(jīng)能夠猜到spawn_opt內(nèi)部應(yīng)該是spawn新的進程了。在spawn_opt函數(shù)中,首先獲取了Parent(proc_lib進程的名字)以及Ancestors,然后調(diào)用erlang模塊的spawn_opt函數(shù)。
這個函數(shù)會調(diào)用當前進程的init_p方法,現(xiàn)在要注意,此時spawn_opt派生了新的進程,那么原來的parent進程會直接返回一個Pid,這個Pid就被我們之前看到的proc_lib:start的地方接收了,并且繼續(xù)向下執(zhí)行了sync_wait(Pid, Timeout)函數(shù),在sync_wait函數(shù)的內(nèi)部在等待接收消息,如果超過timout的時間沒有接收到,就認為失敗了,那么我們可以推測,派生出來的進程一定是去執(zhí)行用戶的init的函數(shù)去了,并且在執(zhí)行完后會發(fā)給parent進程一個消息。
?
我們看看我們的猜測對不對,繼續(xù)跟進到init_p函數(shù)內(nèi)部,我們可以看到函數(shù)內(nèi)部開始搜刮者用戶傳遞進來的Fun的所有信息,并放入進程字典中(真的不喜歡進程字典),然后開始執(zhí)行用戶的Fun函數(shù),然后該進程結(jié)束。
?
是的,你沒有看錯,你沒有發(fā)現(xiàn)任何地方向parent進程發(fā)送了ack信號,這不科學(xué),因為這意味著parent會因為超時而報錯,如果你這么想,你一定是把執(zhí)行的Fun函數(shù)當成了你的init,我們網(wǎng)上看就能發(fā)現(xiàn)實際上是gen模塊的init_it函數(shù),這里還有個不太好的地方就是init_it調(diào)用了函數(shù)init_it2…這命名規(guī)則著實讓人蛋疼……
?
init_it2回調(diào)了用戶傳入的init_it函數(shù),這個函數(shù)是由對應(yīng)的行為模式編寫的init_it函數(shù),比如舉個簡單的例子,在gen_server的源碼中,init_it就調(diào)用了用戶傳入的init函數(shù),并且在執(zhí)行后,調(diào)用init_ack,init_ack函數(shù)向parent進程發(fā)送了一個成功的消息,之后,gen_server進入loop函數(shù)開始接收消息,也就是說,用戶的gen_server:start()函數(shù)執(zhí)行完畢。也印證了我們之前的猜想是正確的。
?
?
其實,這里我有個疑問,不知道作者為什么不把proc_lib:init_ack函數(shù)放在proc_lib的init_p函數(shù)的最后執(zhí)行呢?
?
我們只是簡單的分析了proc_lib的spawn與直接spawn有哪些不同,那么對于我們來說,proc_lib還有哪些直接挖掘的呢?為什么要用proc_lib去包裝spawn呢?
?
翻翻proc_lib的代碼,在上文中,我們也可以看出,在spawn之前,保存了不少信息,比如parent進程的信息和相關(guān)的函數(shù)信息,這些在用procss_info查看時,都可以直觀的看到。同時,模塊向外暴露的initial_call函數(shù),translate_initial_call函數(shù)以及raw_initial_call函數(shù)也可以查看。至于函數(shù)內(nèi)部的實現(xiàn)就不多討論了,比較簡單。
?
最后,我們會發(fā)現(xiàn)還有個有趣的函數(shù)crash_report,我們分析源碼能夠看出,對于proc_lib派生的進程來說,退出信號是normal以及shutdown都會被認為是正常退出。其他的出錯信息會被error_logger模塊捕捉。
?
最后的最后,還有一個非常有意思的函數(shù),就是hibernate函數(shù),這個函數(shù)看起來就讓java程序員很親切,官方手冊上,對erlang模塊的hibernate函數(shù)的解釋大約是把當前正在運行的線程處于一個wait的狀態(tài),此時,進程會拋棄掉自己的調(diào)用棧,并且進行g(shù)c,然后wait,直到信箱中接受到了新的消息。很明顯的,這種情況應(yīng)該是當前進程在一段時間內(nèi)預(yù)計不會收到消息,為了節(jié)省內(nèi)存而觸發(fā)的,在高并發(fā)的場景下對內(nèi)存的節(jié)省會起到一定的作用。同時,文檔上還說,當進程收到新的消息被喚醒,并且將函數(shù)的控制權(quán)交給作為參數(shù)被傳入的Fun。
?
問題出現(xiàn)了,既然調(diào)用棧被拋棄了,那么Fun執(zhí)行完后何去何從?顯然進程這不就直接結(jié)束了?當然不是,proc_lib對hibernate做了個封裝,當被喚醒時會回調(diào)用戶傳入的函數(shù),比如gen_server,那么wake_hib最終會被回調(diào),我們可以看到,在gen_server中,wake_hib在處理完剛剛接受到得消息后,重新回到了handle_msg的函數(shù)中繼續(xù)等待接收消息。
?
對于proc_lib,值得說說的也就這么多,代碼不是很長,大約700多行,這個模塊理解了,本身gen模塊東西也不多,那么所有的otp模式也就比較簡單了。
?
恩,就是這樣。
轉(zhuǎn)載于:https://www.cnblogs.com/chunming-d/p/3766054.html
總結(jié)
以上是生活随笔為你收集整理的[erlang]proc_lib源码浅析的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【原创】QT在嵌入式系统中显示中文的方法
- 下一篇: C++ cin不支持录入空格