golang反编译_【Golang】脱胎换骨的defer(一)
Go語言的defer是一個很方便的機制,能夠把某些函數調用推遲到當前函數返回前才實際執行。我們可以很方便的用defer關閉一個打開的文件、釋放一個Redis連接,或者解鎖一個Mutex。而且Go語言在設計上保證,即使發生panic,所有的defer調用也能夠被執行。不過多個defer函數是按照定義順序倒序執行的。 我們在公眾號有一篇文章:
【Golang】脫胎換骨的defer?mp.weixin.qq.com內容有點兒多,篇幅有點兒長,所以在這里我們打算拆分成四篇文章,每一篇專注一兩個主要問題,可能會好消化些吧~
(一)“ defer如何延遲,因何倒序?”
(二)“ defer函數怎樣傳參?”
(三)“ defer+閉包,再多套幾層,你還hold住嗎?”
(四)“ 都說defer1.12性能有坑,那坑從何來?又該怎么填?”
func f1() {defer A()// code to do something }像這樣一段代碼,在Go1.12中編譯后的偽指令是這樣的(源碼結合反編譯整理出的偽代碼,幫助理解~_~):
func f1() {r := runtime.deferproc(0, A) // 經過recover返回時r為1,否則為0if r > 0 {goto ret}// code to do somethingruntime.deferreturn()return ret:runtime.deferreturn() }其中與defer指令相關的有兩個部分。第一部分是deferproc,它負責保存要執行的函數信息,我們稱之為defer“注冊”。
func deferproc(siz int32, fn *funcval)從函數原型來看,deferproc函數有兩個參數,第一個是被注冊的defer函數的參數加返回值共占多少字節;第二個參數是一個runtime.funcval結構體的指針,也就是一個Function Value。對Function Value感興趣,可以看看這個:
網頁鏈接?mp.weixin.qq.com與defer指令相關的第二部分就是deferreturn,它被編譯器插入到函數返回以前調用,負責執行已經注冊的defer函數。所以defer函數之所以能延遲到函數返回前執行,就是因為先注冊,后調用。
再來看看defer函數為什么會倒序執行。defer注冊信息會保存到defer鏈表。每個goroutine在運行時都對應一個runtime.g結構體,其中有一個_defer字段,保存的就是defer鏈表的頭指針。
deferproc新注冊的defr信息會添加到鏈表頭。deferreturn執行時也從鏈表頭開始,所以defer才會表現為倒序執行。
理解了這些,就可以繼續細化,看看defer注冊時保存了什么信息,defer鏈表中每個元素究竟是什么結構了。
type _defer struct {siz int32started boolsp uintptr // sp at time of deferpc uintptrfn *funcval_panic *_panic // panic that is running deferlink *_defer}siz:由deferproc第一個參數傳入,就是defer函數參數加返回值的總大小。這段空間會直接分配在_defer結構體后面,用于在注冊時保存給defer函數傳入的參數,并在執行時直接拷貝到defer函數的調用者棧上。
started :標識defer函數是否已經開始執行;
sp:就是注冊defer函數的函數棧指針;
pc:是deferproc函數返回后要繼續執行的指令地址;
fn:由deferproc的第二個參數傳入,也就是被注冊的defer函數;
_panic:是觸發defer函數執行的panic指針,正常流程執行defer時它就是nil;
link:自然是鏈到之前注冊的那個_defer結構體。
網頁鏈接?mp.weixin.qq.com總結
以上是生活随笔為你收集整理的golang反编译_【Golang】脱胎换骨的defer(一)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: laravel 控制器中使用中间件_在
- 下一篇: cad填充图案乱理石_CAD软件中如何自