Skip to main content

Command Palette

Search for a command to run...

探究golang的linkname

Published
2 min read

在编写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官方说明

//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.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!

参考:

  • https://forum.golangbridge.org/t/implementation-of-time-sleep/2637
  • https://github.com/golang/go/issues/15006
  • https://golang.org/cmd/compile/
  • https://github.com/golang/go/blob/master/src/runtime/time.go#L47
20 views

More from this blog

2025: 祛魅 灰度 念头通达

今天是2025年的最后一天,当大家都在准备下班的时候,好巧不巧的,我刚好发现了一个不大不小的问题,大胆猜想,小心求证,向上反馈,暴露风险,作为2025年工作注解,实在是再有趣不过了。 今年的工作,从结果上看,还算平稳,至于过程,有太多不可言说的部分。厂里打镙丝的牛马,有工资可拿,理应知足了,至于其它的,与己无关,也没那么重要了。 祛魅 近距离观察大厂,才发现一些违背常识/直觉的事实:路人以为的高大

Feb 28, 20261 min read21

大厂祛魅:破碎的专注力

毁掉一个人最直接的方法,就是毁掉ta的专注力。 这句话的出处已然模糊,但放在大厂环境中,却显得格外深刻。 围城 大厂宛如一座围城。城外的人满怀憧憬,目之所及皆是光鲜;城内的人却如困笼之鸟,翅膀日渐退化,每日挣扎求生。 高大上 不可否认,大厂的硬件设施确实令人艳羡:宽敞的独立园区内,来往穿梭的人群中,几乎人人手握智能设备。这看似现代化的景象背后,却藏着一个无奈的事实:在工作时段,每台电脑都被严密监控,连听音乐都成奢望。于是,工作之余玩手机,成了许多人难得的解压方式。 大厂的品牌效应确实强大。外界对...

Jul 29, 20251 min read138

Black Swan

黑天鹅理论 是指极不可能发生,实际上却又发生的事件 来到大厂打工已经满一个月了,从一开始的手足无措,到逐渐度过不适期,也算是适应了吧。 不适应 刚入职时,不适应的地方还是挺多的。 第一次只使用台式机工作,这就限制了我一天中的绝大部分时间,都必须呆在自己的工位上,好在工位足够大。只是人与人的沟通少了很多,有问题只能在工位上通过 IM 呼对方,有种魔幻又现实的感觉 第一次只能用 Windows,也不能 WSL,这给我的工作效率带来了很大影响,不能用熟悉的软件,就连写代码用的 VSCode 的...

Jan 24, 20251 min read74

2024年: 逐渐平静

这个世界是一面镜子,会把你的感受反射给你 2024 开端: 相由心生 那时,还带着一着愤懑,因为拿到了低绩效,虽然内心知道这是公司经营困难,想让我离开的一种策略,但仍然感受到自己那可笑的自尊受到了践踏。自那之后,非必要不加班,只做份内事,尽可能地不去涉及份外之事。 2024 年中: 与人为善 组里的项目眼见不行了,我被迫去支援 AI 项目,久违地写起了 python,项目接近完成时,意外收到通知:我拿到大礼包了。在这之前,架构师因故裸辞。在我离开之后不到两周,我的 TL 也裸辞了,直到同事告诉...

Jan 9, 20251 min read92

企业软件之殇

殇 动词 未成年而死。 名词 战死者。 笔者经历了两家打着云原生旗号的企业软件/解决方案公司,都是中途加入,项目都以解(失)散(败)告终。 云原生解决方案 NB 公司:一个传统的 IDC 小厂,想着借云原生的热度,进军企业软件市场。 在加入这个项目之前,笔者考取了 CKAD 认证,彼时对 K8s 相当着迷。先简要介绍一下这个项目背景: 基于 Rancher (换皮肤)的二次开发项目,名字叫:HCaaS ,在笔者加入这个团队之前,项目已经开发近两年了,除了 TL 之外,其它人之前都...

Jul 1, 20241 min read103

just for fun

57 posts

I'm a Software Engineer