您好,登錄后才能下訂單哦!
這篇文章主要介紹“如何使用go:linkname指令”,在日常操作中,相信很多人在如何使用go:linkname指令問題上存在疑惑,小編查閱了各式資料,整理出簡單好用的操作方法,希望對大家解答”如何使用go:linkname指令”的疑惑有所幫助!接下來,請跟著小編一起來學習吧!
//go:linkname local remote
remote 可以沒有,此時 remote 使用 local 的值,效果就是 local 被導出。
local 作為占位符,remote 作為實現者
標準庫中的例子:
// 來自 time 包 //go:linkname runtimeNano runtime.nanotime func runtimeNano() int64 // 來自 runtime 包 //go:nosplit func nanotime() int64 { return nanotime1() }
此時二進制文件中并沒有runtimeNano,直接轉化為對runtime.nanotime的調用。
local 作為實現者,remote 作為占位符
同樣來自標準庫。這里存在函數沒有函數體,但是被反向引用。
// 在標準庫的一個 internal 中 //go:linkname runtime_cmpstring runtime.cmpstring func runtime_cmpstring(a, b string) int { l := len(a) if len(b) < l { l = len(b) } for i := 0; i < l; i++ { c1, c2 := a[i], b[i] if c1 < c2 { return -1 } if c1 > c2 { return +1 } } if len(a) < len(b) { return -1 } if len(a) > len(b) { return +1 } return 0 } // 來自 runtime func cmpstring(string, string) int
此時二進制文件中并沒有runtime_cmpstring,對應的函數已經被命名為runtime.cmpstring。也就是說,實現在 internal 包,但最終通過 runtime.cmpstring 來引用。
一個占位符+一個匯編函數
// 在標準庫的一個 internal 中 //go:linkname abigen_runtime_memequal runtime.memequal func abigen_runtime_memequal(a, b unsafe.Pointer, size uintptr) bool
注意runtime.memequal的實現并不在runtime包中,使用匯編實現的話并不要求必須在相應的包中。
# memequal(a, b unsafe.Pointer, size uintptr) bool TEXT runtime·memequal(SB),NOSPLIT,$0-25 MOVQ a+0(FP), SI MOVQ b+8(FP), DI CMPQ SI, DI JEQ eq MOVQ size+16(FP), BX LEAQ ret+24(FP), AX JMP memeqbody<>(SB) eq: MOVB $1, ret+24(FP) RET
兩個常規變量
//go:linkname overflowError runtime.overflowError var overflowError error //go:linkname divideError runtime.divideError var divideError error //go:linkname zeroVal runtime.zeroVal var zeroVal [maxZero]byte //go:linkname _iscgo runtime.iscgo var _iscgo bool = true //go:cgo_import_static x_cgo_setenv //go:linkname x_cgo_setenv x_cgo_setenv //go:linkname _cgo_setenv runtime._cgo_setenv var x_cgo_setenv byte var _cgo_setenv = &x_cgo_setenv //go:cgo_import_static x_cgo_unsetenv //go:linkname x_cgo_unsetenv x_cgo_unsetenv //go:linkname _cgo_unsetenv runtime._cgo_unsetenv var x_cgo_unsetenv byte var _cgo_unsetenv = &x_cgo_unsetenv
一個占位符+一個偽符號
//go:linkname runtime_inittask runtime..inittask var runtime_inittask initTask //go:linkname main_inittask main..inittask var main_inittask initTask
注意是..inittask不是.inittask,而且.inittask只存在于編譯階段,任何包中都無法聲明該變量。
這里額外解釋下 ..inittask 為什么兩個點。第一個點就是普通的 runtime. 這種調用方式,第二個點和 inittask 一起構成一個符號(變量)。注意,Go 中的變量是不允許以 . 開頭的,所以,這個叫偽符號,只在不編譯階段存在。
研究 //go:linkname 是因為如下的背景:
Java 里有 InheritableThreadLocal,SpringWeb 在 ServletActionContext 里使用它,達到在任何地方都能方便的獲取HttpServletRequest。
Go 并沒有提供類似的機制,即使通過 stack 找到 goroutine id(99% 的文章都是這么介紹的),再配合 sync.Map,也只是實現了一個比較粗糙的 ThreadLocal,在子協程里仍然獲取不到父協程的內容。
g.label 雖然不是給這種場景準備的,但它具備了 InheritableThreadLocal 的一切要求,只要我們能夠訪問到 label 私有字段,我們就有了完整版的 InheritableThreadLocal。
下面這個例子是作者真實項目中用的。
在 runtime 和 runtime/pprof 包中有兩個函數:runtime_setProfLabel 和 runtime_getProfLabel。其中,runtime 包中的提供了實現,而 pprof 中的沒有提供實現。如果基于它們創建另外的函數,如下:
//go:linkname SetPointer runtime/pprof.runtime_setProfLabel func SetPointer(ptr unsafe.Pointer) //go:linkname GetPointer runtime/pprof.runtime_getProfLabel func GetPointer() unsafe.Pointer
根據前面的分析,雖然runtime.runtime_setProfLabel/runtime.runtime_getProfLabel提供了函數實現,但是二進制文件中并不會出現(見下方代碼),此時想要調用必須通過runtime/pprof.runtime_setProfLabel/runtime/pprof.runtime_getProfLabel,這也是上面linkname到pprof而不是runtime的根本原因。
// 來自 runtime 包 //go:linkname runtime_setProfLabel runtime/pprof.runtime_setProfLabel func runtime_setProfLabel(labels unsafe.Pointer) { if raceenabled { racereleasemerge(unsafe.Pointer(&labelSync)) } getg().labels = labels } // 來自 runtime/pprof 包 func runtime_setProfLabel(labels unsafe.Pointer) // 來自 runtime 包 //go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel func runtime_getProfLabel() unsafe.Pointer { return getg().labels } // 來自 runtime/pprof 包 func runtime_getProfLabel() unsafe.Pointer
到此,關于“如何使用go:linkname指令”的學習就結束了,希望能夠解決大家的疑惑。理論與實踐的搭配能更好的幫助大家學習,快去試試吧!若想繼續學習更多相關知識,請繼續關注億速云網站,小編會繼續努力為大家帶來更多實用的文章!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。