成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

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

開發 后端
Go 中類似的指令挺多的,比如 Go1.16 中的 //go:embed。前些天有人問我,為什么它用 //go:embed 不起作用?我一看,它是這么寫的:// go:embed,不知道你看到問題了沒有?

 

我之前寫過一篇文章:為什么 Go 標準庫中有些函數只有簽名,沒有函數體?,其中有一點就是 //go:linkname 這個指令。

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

  • // 后不能有空格。有些人可能習慣 // 后不加空格。但一般認為,// 后應該加一個空格。不過 go 指令卻要求不能有空格,這是一個小“坑”,得注意。所以上面那位朋友就是加了空格,導致出問題。(程序并不會報錯,只是沒有得到自己想要的結果)
  • 代碼和指令之間不能有空行或其他注釋。這一點應該還好,很多人不會用錯吧;
  • 一般來說,使用指令需要導入相應的包。比如 //go:linkname 指令要求導入 unsafe 包,一般會 import _ "unsafe”,//go:embed 指令,要求導入 embed 包。

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

大佬好,能不能寫一篇 linkname 的文章。目前已經有了一些初步概念,但是尚有一些疑團不是特別清晰。

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

譬如 //go:linkname runtimeNano runtime.nanotime,runtimeNano 作為占位符 runtime.nanotime 提供實現,任何調用 runtimeNano 的地方實際替換為對 runtime.nanotime 的調用,這種場景比較容易接受。

譬如 //go:linkname runtime_cmpstring runtime.cmpstring,runtime_cmpstring 提供實現 runtime.cmpstring作為占位符,是不是這時符號表里不存在 runtime_cmpstring 只有 runtime.cmpstring?

經過簡單溝通,他寫了一篇文章解決自己的困惑。希望對各位有幫助。以下是他寫的關于 //go:linkname 的文章(我做了一些調整)。

01 格式

  1. //go:linkname local remote 

remote 可以沒有,此時 remote 使用 local 的值,效果就是 local 被導出。

02 local 和 remote 同時為函數

local 作為占位符,remote 作為實現者

標準庫中的例子:

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

此時二進制文件中并沒有runtimeNano,直接轉化為對runtime.nanotime的調用。

local 作為實現者,remote 作為占位符

同樣來自標準庫。這里存在函數沒有函數體,但是被反向引用。

  1. // 在標準庫的一個 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. // 來自 runtime 
  26. func cmpstring(string, string) int 

此時二進制文件中并沒有runtime_cmpstring,對應的函數已經被命名為runtime.cmpstring。也就是說,實現在 internal 包,但最終通過 runtime.cmpstring 來引用。

一個占位符+一個匯編函數

  1. // 在標準庫的一個 internal 中 
  2. //go:linkname abigen_runtime_memequal runtime.memequal 
  3. func abigen_runtime_memequal(a, b unsafe.Pointer, size uintptr) bool 

注意runtime.memequal的實現并不在runtime包中,使用匯編實現的話并不要求必須在相應的包中。

  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 同時為變量

兩個常規變量

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

一個占位符+一個偽符號

  1. //go:linkname runtime_inittask runtime..inittask 
  2. var runtime_inittask initTask 
  3.  
  4. //go:linkname main_inittask main..inittask 
  5. var main_inittask initTask 

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

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

04 一個例子

研究 //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 中的沒有提供實現。如果基于它們創建另外的函數,如下:

  1. //go:linkname SetPointer runtime/pprof.runtime_setProfLabel 
  2. func SetPointer(ptr unsafe.Pointer) 
  3.  
  4. //go:linkname GetPointer runtime/pprof.runtime_getProfLabel 
  5. func GetPointer() unsafe.Pointer 

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

  1. // 來自 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. // 來自 runtime/pprof 包 
  10. func runtime_setProfLabel(labels unsafe.Pointer) 
  11.  
  12. // 來自 runtime 包 
  13. //go:linkname runtime_getProfLabel runtime/pprof.runtime_getProfLabel 
  14. func runtime_getProfLabel() unsafe.Pointer { 
  15.  return getg().labels 
  16.  
  17. // 來自 runtime/pprof 包 
  18. func runtime_getProfLabel() unsafe.Pointer 

05 總結

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

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

本文轉載自微信公眾號「polarisxu」,可以通過以下二維碼關注。轉載本文請聯系polarisxu公眾號。

 

責任編輯:武曉燕 來源: polarisxu
相關推薦

2019-10-28 10:19:27

JVM 類加載器Java

2024-04-26 00:01:00

Go語言類型

2023-01-09 08:14:08

GoHttpServer

2023-03-14 09:03:20

Go語法腳本

2022-11-09 09:15:31

ProtoBufGo語言

2021-02-02 09:10:12

Go語言二進制

2021-12-20 07:59:07

Go語言結構體

2021-09-27 07:39:52

Go初始化函數package

2023-06-01 16:27:34

匯編語言函數

2020-03-31 14:40:24

HashMap源碼Java

2020-08-27 07:34:50

Zookeeper數據結構

2024-04-12 12:19:08

語言模型AI

2023-11-25 09:41:34

GogRPCHandler

2021-01-26 05:19:56

語言Go Context

2020-10-26 09:18:50

RedisCluste

2023-12-22 19:59:15

2022-03-24 08:51:48

Redis互聯網NoSQL

2021-08-04 16:06:45

DataOps智領云

2024-03-26 00:17:51

Go語言IO

2023-04-26 15:43:24

容器編排容器編排工具
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 久久在线 | 欧美亚洲视频 | 91看片网 | 亚洲精品一二区 | 91高清免费| 亚洲一区二区在线视频 | 97在线观视频免费观看 | 日韩不卡一区二区 | 久久久婷婷 | 暴草美女| 日韩欧美网 | 日本久久精品视频 | 欧美激情a∨在线视频播放 成人免费共享视频 | 中文字幕一区在线观看视频 | 亚洲午夜精品在线观看 | 日韩不卡在线观看 | 99精品国产成人一区二区 | 亚洲高清视频在线 | 成人精品鲁一区一区二区 | 亚洲欧美一区二区三区在线 | 精品国产一级 | 伊人精品一区二区三区 | 久久99视频这里只有精品 | 精品一区二区三区中文字幕 | 日韩在线免费视频 | 欧美另类视频在线 | 日韩美女爱爱 | 碰碰视频 | 日韩在线不卡视频 | 欧美成人一区二区三区片免费 | 日本精品视频在线观看 | 国产欧美一区二区三区在线看蜜臀 | 97超碰中文网 | 在线观看av免费 | 久久精品亚洲成在人线av网址 | 日本精品视频 | 日韩精品一区二区久久 | 国产精品一区一区 | 欧美另类视频 | a爱视频| 91.色|