溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

如何使用go:linkname指令

發(fā)布時(shí)間:2021-10-13 11:37:07 來(lái)源:億速云 閱讀:355 作者:iii 欄目:編程語(yǔ)言

這篇文章主要介紹“如何使用go:linkname指令”,在日常操作中,相信很多人在如何使用go:linkname指令問(wèn)題上存在疑惑,小編查閱了各式資料,整理出簡(jiǎn)單好用的操作方法,希望對(duì)大家解答”如何使用go:linkname指令”的疑惑有所幫助!接下來(lái),請(qǐng)跟著小編一起來(lái)學(xué)習(xí)吧!

 如何使用go:linkname指令

01 格式

//go:linkname local remote

remote 可以沒(méi)有,此時(shí) remote 使用 local 的值,效果就是 local 被導(dǎo)出。

02 local 和 remote 同時(shí)為函數(shù)

local 作為占位符,remote 作為實(shí)現(xiàn)者

標(biāo)準(zhǔn)庫(kù)中的例子:

// 來(lái)自 time 包 //go:linkname runtimeNano runtime.nanotime func runtimeNano() int64  // 來(lái)自 runtime 包 //go:nosplit func nanotime() int64 {  return nanotime1() }

此時(shí)二進(jìn)制文件中并沒(méi)有runtimeNano,直接轉(zhuǎn)化為對(duì)runtime.nanotime的調(diào)用。

local 作為實(shí)現(xiàn)者,remote 作為占位符

同樣來(lái)自標(biāo)準(zhǔn)庫(kù)。這里存在函數(shù)沒(méi)有函數(shù)體,但是被反向引用。

// 在標(biāo)準(zhǔn)庫(kù)的一個(gè) 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 }  // 來(lái)自 runtime func cmpstring(string, string) int

此時(shí)二進(jìn)制文件中并沒(méi)有runtime_cmpstring,對(duì)應(yīng)的函數(shù)已經(jīng)被命名為runtime.cmpstring。也就是說(shuō),實(shí)現(xiàn)在 internal  包,但最終通過(guò) runtime.cmpstring 來(lái)引用。

一個(gè)占位符+一個(gè)匯編函數(shù)

// 在標(biāo)準(zhǔn)庫(kù)的一個(gè) internal 中 //go:linkname abigen_runtime_memequal runtime.memequal func abigen_runtime_memequal(a, b unsafe.Pointer, size uintptr) bool

注意runtime.memequal的實(shí)現(xiàn)并不在runtime包中,使用匯編實(shí)現(xiàn)的話并不要求必須在相應(yīng)的包中。

# memequal(a, b unsafe.Pointer, size uintptr) bool TEXT runtime&middot;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

03 local 和 remote 同時(shí)為變量

兩個(gè)常規(guī)變量

//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

一個(gè)占位符+一個(gè)偽符號(hào)

//go:linkname runtime_inittask runtime..inittask var runtime_inittask initTask  //go:linkname main_inittask main..inittask var main_inittask initTask

注意是..inittask不是.inittask,而且.inittask只存在于編譯階段,任何包中都無(wú)法聲明該變量。

這里額外解釋下 ..inittask 為什么兩個(gè)點(diǎn)。第一個(gè)點(diǎn)就是普通的 runtime. 這種調(diào)用方式,第二個(gè)點(diǎn)和 inittask  一起構(gòu)成一個(gè)符號(hào)(變量)。注意,Go 中的變量是不允許以 . 開(kāi)頭的,所以,這個(gè)叫偽符號(hào),只在不編譯階段存在。

04 一個(gè)例子

研究 //go:linkname 是因?yàn)槿缦碌谋尘埃?/p>

Java 里有 InheritableThreadLocal,SpringWeb 在 ServletActionContext  里使用它,達(dá)到在任何地方都能方便的獲取HttpServletRequest。

Go 并沒(méi)有提供類似的機(jī)制,即使通過(guò) stack 找到 goroutine id(99% 的文章都是這么介紹的),再配合  sync.Map,也只是實(shí)現(xiàn)了一個(gè)比較粗糙的 ThreadLocal,在子協(xié)程里仍然獲取不到父協(xié)程的內(nèi)容。

g.label 雖然不是給這種場(chǎng)景準(zhǔn)備的,但它具備了 InheritableThreadLocal 的一切要求,只要我們能夠訪問(wèn)到 label  私有字段,我們就有了完整版的 InheritableThreadLocal。

下面這個(gè)例子是作者真實(shí)項(xiàng)目中用的。

在 runtime 和 runtime/pprof 包中有兩個(gè)函數(shù):runtime_setProfLabel 和  runtime_getProfLabel。其中,runtime 包中的提供了實(shí)現(xiàn),而 pprof 中的沒(méi)有提供實(shí)現(xiàn)。如果基于它們創(chuàng)建另外的函數(shù),如下:

//go:linkname SetPointer runtime/pprof.runtime_setProfLabel func SetPointer(ptr unsafe.Pointer)  //go:linkname GetPointer runtime/pprof.runtime_getProfLabel func GetPointer() unsafe.Pointer

根據(jù)前面的分析,雖然runtime.runtime_setProfLabel/runtime.runtime_getProfLabel提供了函數(shù)實(shí)現(xiàn),但是二進(jìn)制文件中并不會(huì)出現(xiàn)(見(jiàn)下方代碼),此時(shí)想要調(diào)用必須通過(guò)runtime/pprof.runtime_setProfLabel/runtime/pprof.runtime_getProfLabel,這也是上面linkname到pprof而不是runtime的根本原因。

// 來(lái)自 runtime 包 //go:linkname runtime_setProfLabel runtime/pprof.runtime_setProfLabel func runtime_setProfLabel(labels unsafe.Pointer) {  if raceenabled {   racereleasemerge(unsafe.Pointer(&labelSync))  }  getg().labels = labels }  // 來(lái)自 runtime/pprof 包 func runtime_setProfLabel(labels unsafe.Pointer)  // 來(lái)自 runtime 包 //go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel func runtime_getProfLabel() unsafe.Pointer {  return getg().labels }  // 來(lái)自 runtime/pprof 包 func runtime_getProfLabel() unsafe.Pointer

到此,關(guān)于“如何使用go:linkname指令”的學(xué)習(xí)就結(jié)束了,希望能夠解決大家的疑惑。理論與實(shí)踐的搭配能更好的幫助大家學(xué)習(xí),快去試試吧!若想繼續(xù)學(xué)習(xí)更多相關(guān)知識(shí),請(qǐng)繼續(xù)關(guān)注億速云網(wǎng)站,小編會(huì)繼續(xù)努力為大家?guī)?lái)更多實(shí)用的文章!

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

go
AI