2016 年 4 月 28 日
名字在 golang 中的重要性和在其他任何一種語言是一樣的。他們甚至含有語義的作用:在一個包的外部某個名字的可見性是由這個名字首字母是否是大寫來決定的。
有時為了更好的組織代碼或者在其他包使用某些隱藏的函數時需要克服這種限制。
在過去美好的日子,有 2 種實現方式,它們能繞過編譯器的檢查:不能引用未導出的名稱 pkg.symbol :
舊的方式,現在已經不再使用 - 彙編級隱式連接到所需符號,稱為 assembly stubs ,詳見 go runtime, os/signal: use //go:linkname instead of assembly stubs to get access to runtime functions 。現行的方式 - go 編譯器通過 go:linkname 支持名稱重定向,引用於 11.11.14 dev.cc code review 169360043: cmd/gc: changes for removing runtime C code (issue 169360043 by r…@golang.org) ,在 github.com 的 issue 上有可以找到 cmd/compile: 「missing function body」 error when using the //go:linkname compiler directive #15006 。用這些技巧我曾設法綁定 golang 運行時調度器相關的函數用以減少過度使用 go 的協程和內部鎖機制導致的 gc 停頓。
使用 assembly stubs
想法很簡單 - 為需要的標識符提供直接跳轉彙編指令 stubs 。連結器並不知道標識符是否已導出。
詳見舊版的代碼 src/os/signal/sig.s :
// Assembly to get into package runtime without using exported symbols.// +build amd64 amd64p32 arm arm64 386 ppc64 ppc64le#include "textflag.h"#ifdef GOARCH_arm#define JMP B#endif#ifdef GOARCH_ppc64#define JMP BR#endif#ifdef GOARCH_ppc64le#define JMP BR#endifTEXT ·signal_disable(SB),NOSPLIT,$0JMP runtime·signal_disable(SB)TEXT ·signal_enable(SB),NOSPLIT,$0JMP runtime·signal_enable(SB)TEXT ·signal_ignore(SB),NOSPLIT,$0JMP runtime·signal_ignore(SB)TEXT ·signal_recv(SB),NOSPLIT,$0JMP runtime·signal_recv(SB)
而 signal_unix.go 的綁定如下:
// +build darwin dragonfly freebsd linux nacl netbsd openbsd solaris windowspackage signalimport ( "os" "syscall")// In assembly.func signal_disable(uint32)func signal_enable(uint32)func signal_ignore(uint32)func signal_recv() uint32
使用 go:linkname
為了使用這種方法,代碼中必須 import _ "unsafe" 包。為了解決 go 編譯器 -complete 參數的限制,一種可能的方法是在 main 包目錄加一個空的彙編 stub 文件以禁用編譯器的檢查。
詳見 os/signal/sig.s:
// The runtime package uses //go:linkname to push a few functions into this// package but we still need a .s file so the Go tool does not pass -complete// to the go tool compile so the latter does not complain about Go functions// with no bodies.
這個指令的格式是 //go:linkname localname linkname。使用這種方法可以將新的標識符連結(導出)或綁定到已存在的標識符(導入)。
用 go:linkname 導出
在 runtime/proc.go 中一個函數的實現
...//go:linkname sync_runtime_doSpin sync.runtime_doSpin//go:nosplitfunc sync_runtime_doSpin() { procyield(active_spin_cnt)}
這裡明確地向編譯器指示,將另一個名字添加到 sync 包的 runtime_doSpin 函數代碼中。並且 sync 包簡單的重用了在 sync/runtime.go 中的代碼:
package syncimport "unsafe"...// runtime_doSpin does active spinning.func runtime_doSpin()
用 go:linkname 導入
在 net/parse.go 中有一個很好的例子:
package netimport ( ... _ "unsafe" // For go:linkname)...// byteIndex is strings.IndexByte. It returns the index of the// first instance of c in s, or -1 if c is not present in s.// strings.IndexByte is implemented in runtime/asm_$GOARCH.s//go:linkname byteIndex strings.IndexBytefunc byteIndex(s string, c byte) int
使用這種技巧的方法:
import _ "unsafe" 包。
提供一個沒有函數體的函數,比如: func byteIndex(s string, c byte) int
在定義函數前,正確的放上 //go:linkname 指令,例如 //go:linkname byteIndex strings.IndexByte,byteIndex 是本地名稱,strings.IndexByte 是遠程名稱。
提供 .s 後綴的 stub 文件,以便編譯器繞過 -complete 的檢查,允許不完整的函數定義(譯註:指沒有函數體)。
例子 goparkunlock
package mainimport ( _ "unsafe" "fmt" "runtime/pprof" "os" "time")// Event types in the trace, args are given in square brackets.const ( traceEvGoBlock = 20 // goroutine blocks [timestamp, stack])type mutex struct { // Futex-based impl treats it as uint32 key, // while sema-based impl as M* waitm. // Used to be a union, but unions break precise GC. key uintptr}//go:linkname lock runtime.lockfunc lock(l *mutex)//go:linkname unlock runtime.unlockfunc unlock(l *mutex)//go:linkname goparkunlock runtime.goparkunlockfunc goparkunlock(lock *mutex, reason string, traceEv byte, traceskip int)func main() { l := &mutex{} go func() { lock(l) goparkunlock(l, "xxx", traceEvGoBlock, 1) }() for { pprof.Lookup("goroutine").WriteTo(os.Stdout, 1) time.Sleep(time.Second * 1) }}
源碼
可在這裡獲取 https://github.com/sitano/gsysint 。
相關文章
Docker Windows install instructions on the state of 4 August 2016 04 Aug 2016PowerShell ducklish typed 25 Apr 2016Approach into strong typed configuration management DSL with FAKE, F#, WinRM and PowerShell 15 Mar 2016via: https://sitano.github.io/2016/04/28/golang-private/
作者:JohnKoepi 譯者:kekemuyu 校對:polaris1119
本文由 GCTT 原創編譯,Go語言中文網 榮譽推出
本文由 GCTT 原創翻譯,Go語言中文網 首發。也想加入譯者行列,為開源做一些自己的貢獻麼?歡迎加入 GCTT!
翻譯工作和譯文發表僅用於學習和交流目的,翻譯工作遵照 CC-BY-NC-SA 協議規定,如果我們的工作有侵犯到您的權益,請及時聯繫我們。
歡迎遵照 CC-BY-NC-SA 協議規定 轉載,敬請在正文中標註並保留原文/譯文連結和作者/譯者等信息。
文章僅代表作者的知識和看法,如有不同觀點,請樓下排隊吐槽