為什么 Go 標准庫中有些函數只有簽名,沒有函數體?


如果你看過 Go 語言標准庫,應該有見到過,有一些函數只有簽名,沒有函數體。你有沒有感覺到很奇怪?這到底是怎么回事?我們自己可以這么做嗎?本文就來解密它。

首先,函數肯定得有實現,沒有函數體,一定是在其他某個地方。Go 中一般有兩種形式。

函數簽名使用Go,然后通過該包中的匯編文件來實現它

比如,在標准庫 sync/atomic 包中的函數基本只有函數簽名。比如:atomic.StoreInt32

// StoreInt32 atomically stores val into *addr.
func StoreInt32(addr *int32, val int32)

它的函數實現在哪呢?其實只要稍微留意一下發現該目錄下有一個文件:asm.s,它提供了具體的實現,即通過匯編來實現:

TEXT ·StoreInt32(SB),NOSPLIT,$0
    JMP    runtime∕internal∕atomic·Store(SB)

具體的實現,在 runtime∕internal 文件夾中,有興趣你可以打開 asm_amd64.s 看看。

很明顯,這種方式一方面會是效率的考慮,另一方面,有一些代碼只能匯編實現。
以上方式,你自己也可以嘗試。比如實現一個 Sum(a, b int) int。歡迎評論給出你的代碼。

通過//go:linkname指令來實現

比如,在標准庫 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)

它的實現在哪里呢?在 time 包中並沒有找到相應的匯編文件。

按照 Go 源碼的風格,這時候一般需要去 runtime 包中找。我們會找到 time.go,其中有一個函數:

// timeSleep puts the current goroutine to sleep for at least ns nanoseconds.
//go:linkname timeSleep time.Sleep
func timeSleep(ns int64) {
    ...
}

這就是我們要找的 time.Sleep 的實現。

如果你有認真跟着學習「每日一學」,對於 //go:linkname 應該不陌生,這里的關鍵就在於這個指令,它的格式是:

//go:linkname 函數名 包名.函數名

因此我們在遇到函數沒有實現,但匯編又不存在時,可以通過嘗試搜索:go:linkname xxx xx.xxx 的形式來找,比如 time.Sleep 就可以通過 //go:linkname timeSleep time.Sleep 來查找具體實現在哪。

這里面要提示一點,使用 //go:linkname,必須導入 unsafe 包,所以,有時候會見到:import _ "unsafe" 這樣的代碼。

一般來說,我們自己的代碼不會使用這樣的方式,但你會寫一個示例試試嗎?歡迎評論給出你的代碼。

另外,想想為什么 time.Sleep 的實現要這么搞?

轉載自:
為什么 Go 標准庫中有些函數只有簽名,沒有函數體?


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM