探究golang的linkname用法
在編寫golang程式的過程中,會經常有一些sleep的需求,於是我們使用time.Sleep
函式
跳轉到函式定義處發現這個函式定義如下:
// Sleep pauses the current goroutine for at least the duration d. // A negative or zero duration causes Sleep to return immediately. func Sleep(d Duration)
沒錯,只有定義沒有實現?顯然不是,函式的實現在runtime/time.go
// timeSleep puts the current goroutine to sleep for at least ns nanoseconds. //go:linkname timeSleep time.Sleep func timeSleep(ns int64) { if ns <= 0 { return } gp := getg() t := gp.timer if t == nil { t = new(timer) gp.timer = t } *t = timer{} t.when = nanotime() + ns t.f = goroutineReady t.arg = gp tb := t.assignBucket() lock(&tb.lock) if !tb.addtimerLocked(t) { unlock(&tb.lock) badTimer() } goparkunlock(&tb.lock, waitReasonSleep, traceEvGoSleep, 2) }
看看go:linkname
的ofollow,noindex">官方說明
//go:linkname localname importpath.name The //go:linkname directive instructs the compiler to use “importpath.name” as the object file symbol name for the variable or function declared as “localname” in the source code. Because this directive can subvert the type system and package modularity, it is only enabled in files that have imported "unsafe".
這個指令告訴編譯器為當前原始檔中私有函式或者變數在編譯時連結到指定的方法或變數。因為這個指令破壞了型別系統和包的模組化,因此在使用時必須匯入unsafe
包,所以可以看到runtime/time.go
檔案是有匯入unsafe
包的。
我們看到go:linkname
的格式,這裡localname
自然對應timeSleep
,importpath.name
就對應time.Sleep
,但為什麼要這麼做呢?
我們知道time.Sleep
在time
包裡,是可匯出,而timeSleep
在runtime
包裡面,是不可匯出了,那麼go:linkname
的意義在於讓time
可以呼叫runtime
中原本不可匯出的函式,有點hack
,舉個栗子:
目錄結構如下
➜demo git:(master) ✗ tree . ├── linkname │└── a.go ├── main.go └── outer └── world.go
檔案內容 a.go
package linkname import _ "unsafe" //go:linkname hello examples/demo/outer.World func hello() { println("hello,world!") }
world.go
package outer import ( _ "examples/demo/linkname" ) func World()
main.go
package main import ( "examples/demo/outer" ) func main() { outer.World() }
執行如下:
# examples/demo/outer outer/world.go:7:6: missing function body
難道理解錯了,這是因為go build
預設加會加上-complete
引數,這個引數檢查到World()
沒有方法體,在outer
資料夾中增加一個空的.s
檔案即可繞過這個限制
➜demo git:(master) ✗ tree . ├── linkname │└── a.go ├── main.go └── outer ├── i.s └── world.go
輸出如下:
hello,world!
參考: