在golang中,为了性能的目的,当前执行的g
是保存在当前线程的TLS中的,而TLS的地址在结构体m
里面。问题是怎么放进去的呢?
可以从程序的启动入手,顺藤摸瓜。
编写一个打印hello,world
的程序
// hello.go
package main
func main() {
print("hello, world")
}
编译生成可执行文件
go build -o hello hello.go
用gdb进行调试,找到程序的入口 _rt0_amd64_linux
gdb hello
(gdb) info files
...
Entry point: 0x448f20
...
(gdb) list *0x448f20
0x448f20 is in _rt0_amd64_linux (/home/zenk/tools/goroot/src/runtime/rt0_linux_amd64.s:8)
3 // license that can be found in the LICENSE file.
4
5 #include "textflag.h"
6
7 TEXT _rt0_amd64_linux(SB),NOSPLIT,$-8
8 LEAQ 8(SP), SI // argv
9 MOVQ 0(SP), DI // argc
10 MOVQ $main(SB), AX
11 JMP AX
12
发现_rt0_amd64_linux
调用了main
函数,后者调用了runtime.rt0_go
。而在函数runtime.rt0_go
中
127 LEAQ runtime·m0+m_tls(SB), DI
128 CALL runtime·settls(SB)
把m0.tls
的地址放到寄存器DI
,并调用了函数runtime.settls
,查看runtime.settls
核心代码
503 ADDQ $8, DI // ELF wants to use -8(FS)
504 #endif
505 MOVQ DI, SI
506 MOVQ $0x1002, DI // ARCH_SET_FS
507 MOVQ $158, AX // arch_prctl
508 SYSCALL
可以看到,这里调用了系统调用arch_prctl
,在linux下把m0.tls+8
的地址保存到fs
寄存器。到此,完成TLS的设置。