av激情亚洲男人的天堂国语,日韩欧美精品一中文字幕,无码av一区二区三区无码,国产又色又爽又刺激的a片,国产又色又爽又刺激的a片

注釋竟然還有特殊用途?一文解惑//go:linkname指令

我之前寫(xiě)過(guò)一篇文章:為什么 Go 標(biāo)準(zhǔn)庫(kù)中有些函數(shù)只有簽名,沒(méi)有函數(shù)體?,其中有一點(diǎn)就是 //go:linkname 這個(gè)指令。

目前創(chuàng)新互聯(lián)已為上1000+的企業(yè)提供了網(wǎng)站建設(shè)、域名、雅安服務(wù)器托管、成都網(wǎng)站托管、企業(yè)網(wǎng)站設(shè)計(jì)、五河網(wǎng)站維護(hù)等服務(wù),公司將堅(jiān)持客戶導(dǎo)向、應(yīng)用為本的策略,正道將秉承"和諧、參與、激情"的文化,與客戶和合作伙伴齊心協(xié)力一起成長(zhǎng),共同發(fā)展。

Go 中類(lèi)似的指令挺多的,比如 Go1.16 中的 //go:embed。前些天有人問(wèn)我,為什么它用 //go:embed 不起作用?我一看,它是這么寫(xiě)的:// go:embed,不知道你看到問(wèn)題了沒(méi)有?是的,指令是通過(guò)注釋的方式,但有三點(diǎn)要求,要特別注意:

  • // 后不能有空格。有些人可能習(xí)慣 // 后不加空格。但一般認(rèn)為,// 后應(yīng)該加一個(gè)空格。不過(guò) go 指令卻要求不能有空格,這是一個(gè)小“坑”,得注意。所以上面那位朋友就是加了空格,導(dǎo)致出問(wèn)題。(程序并不會(huì)報(bào)錯(cuò),只是沒(méi)有得到自己想要的結(jié)果)
  • 代碼和指令之間不能有空行或其他注釋。這一點(diǎn)應(yīng)該還好,很多人不會(huì)用錯(cuò)吧;
  • 一般來(lái)說(shuō),使用指令需要導(dǎo)入相應(yīng)的包。比如 //go:linkname 指令要求導(dǎo)入 unsafe 包,一般會(huì) import _ "unsafe”,//go:embed 指令,要求導(dǎo)入 embed 包。

有另外一位 Go 朋友「橘中秘士」微信私聊我:

大佬好,能不能寫(xiě)一篇 linkname 的文章。目前已經(jīng)有了一些初步概念,但是尚有一些疑團(tuán)不是特別清晰。

//go:linkname localname remotename,其中 local 作為占位符 remote 作為實(shí)現(xiàn)者或者 local 作為實(shí)現(xiàn)者 remote 作為占位符都是可以的。目前理解的就是給 Symbol 添加了一個(gè) Linkname,查找 Symbo l的時(shí)候用 remote。

譬如 //go:linkname runtimeNano runtime.nanotime,runtimeNano 作為占位符 runtime.nanotime 提供實(shí)現(xiàn),任何調(diào)用 runtimeNano 的地方實(shí)際替換為對(duì) runtime.nanotime 的調(diào)用,這種場(chǎng)景比較容易接受。

譬如 //go:linkname runtime_cmpstring runtime.cmpstring,runtime_cmpstring 提供實(shí)現(xiàn) runtime.cmpstring作為占位符,是不是這時(shí)符號(hào)表里不存在 runtime_cmpstring 只有 runtime.cmpstring?

經(jīng)過(guò)簡(jiǎn)單溝通,他寫(xiě)了一篇文章解決自己的困惑。希望對(duì)各位有幫助。以下是他寫(xiě)的關(guān)于 //go:linkname 的文章(我做了一些調(diào)整)。

01 格式

 
 
 
 
  1. //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ù)中的例子:

 
 
 
 
  1. // 來(lái)自 time 包
  2. //go:linkname runtimeNano runtime.nanotime
  3. func runtimeNano() int64
  4. // 來(lái)自 runtime 包
  5. //go:nosplit
  6. func nanotime() int64 {
  7.  return nanotime1()
  8. }

此時(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ù)體,但是被反向引用。

 
 
 
 
  1. // 在標(biāo)準(zhǔn)庫(kù)的一個(gè) internal 中
  2. //go:linkname runtime_cmpstring runtime.cmpstring
  3. func runtime_cmpstring(a, b string) int {
  4.  l := len(a)
  5.  if len(b) < l {
  6.   l = len(b)
  7.  }
  8.  for i := 0; i < l; i++ {
  9.   c1, c2 := a[i], b[i]
  10.   if c1 < c2 {
  11.    return -1
  12.   }
  13.   if c1 > c2 {
  14.    return +1
  15.   }
  16.  }
  17.  if len(a) < len(b) {
  18.   return -1
  19.  }
  20.  if len(a) > len(b) {
  21.   return +1
  22.  }
  23.  return 0
  24. }
  25. // 來(lái)自 runtime
  26. 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ù)

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

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

 
 
 
 
  1. # memequal(a, b unsafe.Pointer, size uintptr) bool
  2. TEXT runtime·memequal(SB),NOSPLIT,$0-25
  3.     MOVQ    a+0(FP), SI
  4.     MOVQ    b+8(FP), DI
  5.     CMPQ    SI, DI
  6.     JEQ eq
  7.     MOVQ    size+16(FP), BX
  8.     LEAQ    ret+24(FP), AX
  9.     JMP memeqbody<>(SB)
  10. eq:
  11.     MOVB    $1, ret+24(FP)
  12.     RET

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

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

 
 
 
 
  1. //go:linkname overflowError runtime.overflowError
  2. var overflowError error
  3. //go:linkname divideError runtime.divideError
  4. var divideError error
  5. //go:linkname zeroVal runtime.zeroVal
  6. var zeroVal [maxZero]byte
  7. //go:linkname _iscgo runtime.iscgo
  8. var _iscgo bool = true
  9. //go:cgo_import_static x_cgo_setenv
  10. //go:linkname x_cgo_setenv x_cgo_setenv
  11. //go:linkname _cgo_setenv runtime._cgo_setenv
  12. var x_cgo_setenv byte
  13. var _cgo_setenv = &x_cgo_setenv
  14. //go:cgo_import_static x_cgo_unsetenv
  15. //go:linkname x_cgo_unsetenv x_cgo_unsetenv
  16. //go:linkname _cgo_unsetenv runtime._cgo_unsetenv
  17. var x_cgo_unsetenv byte
  18. var _cgo_unsetenv = &x_cgo_unsetenv

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

 
 
 
 
  1. //go:linkname runtime_inittask runtime..inittask
  2. var runtime_inittask initTask
  3. //go:linkname main_inittask main..inittask
  4. 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)有提供類(lè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ù),如下:

 
 
 
 
  1. //go:linkname SetPointer runtime/pprof.runtime_setProfLabel
  2. func SetPointer(ptr unsafe.Pointer)
  3. //go:linkname GetPointer runtime/pprof.runtime_getProfLabel
  4. 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的根本原因。

 
 
 
 
  1. // 來(lái)自 runtime 包
  2. //go:linkname runtime_setProfLabel runtime/pprof.runtime_setProfLabel
  3. func runtime_setProfLabel(labels unsafe.Pointer) {
  4.  if raceenabled {
  5.   racereleasemerge(unsafe.Pointer(&labelSync))
  6.  }
  7.  getg().labels = labels
  8. }
  9. // 來(lái)自 runtime/pprof 包
  10. func runtime_setProfLabel(labels unsafe.Pointer)
  11. // 來(lái)自 runtime 包
  12. //go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel
  13. func runtime_getProfLabel() unsafe.Pointer {
  14.  return getg().labels
  15. }
  16. // 來(lái)自 runtime/pprof 包
  17. func runtime_getProfLabel() unsafe.Pointer

05 總結(jié)

Go 中有不少指令,有些指令你可能不太需要關(guān)心,也不會(huì)用到。然而有些指令了解它們的意思,對(duì)閱讀相關(guān)代碼很有幫助。

這篇文章全面介紹了 //go:linkname 指令,不知道是否徹底解除了你的疑惑?歡迎留言交流!

本文轉(zhuǎn)載自微信公眾號(hào)「polarisxu」,可以通過(guò)以下二維碼關(guān)注。轉(zhuǎn)載本文請(qǐng)聯(lián)系polarisxu公眾號(hào)。


分享標(biāo)題:注釋竟然還有特殊用途?一文解惑//go:linkname指令
文章路徑:http://uogjgqi.cn/article/coedecp.html
掃二維碼與項(xiàng)目經(jīng)理溝通

我們?cè)谖⑿派?4小時(shí)期待你的聲音

解答本文疑問(wèn)/技術(shù)咨詢(xún)/運(yùn)營(yíng)咨詢(xún)/技術(shù)建議/互聯(lián)網(wǎng)交流