在編寫golang程序的過程中,會經常有一些sleep的需求,于是我們使用time.Sleep函數
跳轉到函數定義處發(fā)現這個函數定義如下:
// 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的官方說明
//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 “l(fā)ocalname” 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".
這個指令告訴編譯器為當前源文件中私有函數或者變量在編譯時鏈接到指定的方法或變量。因為這個指令破壞了類型系統(tǒng)和包的模塊化,因此在使用時必須導入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!
參考: