探究golang的linkname

在編寫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.Sleeptime包里,是可導出,而timeSleepruntime包里面,是不可導出了,那么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!

參考:

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • this總是指向一個對象,有時指向全局對象,有時指向構造對象,有時指向DOM對象 一、javascript中的th...
    廊橋夢醉閱讀 1,109評論 0 1
  • 寶寶咳嗽持續(xù)不停,幾乎每個媽媽都有深受其害的經歷,中醫(yī)上講:咳嗽有痰,病邪初期在肺,中期在脾,后期在腎,所以在小...
    b354d0d846b3閱讀 539評論 0 0
  • 山城是座城,也或許是個人,他走著走著,就把自己丟了。 ——題記 (一) ...
    姬皮爾伯格閱讀 325評論 2 1
  • 看了場電影,哭了一鼻子。咳嗽,早點休息。
    陳琦不黑閱讀 305評論 1 0

友情鏈接更多精彩內容