go当中的线程存储与使用
1、settls
从引导代码中可以看到,在执行settls前将m.tls放入了DI。
go/src/runtime/asm_amd64.s:159
TEXT runtime·rt0_go(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 .... LEAQ runtime·m0+m_tls(SB), DI CALL runtime·settls(SB) ....
tls定义是无符号整形指针
type m struct { ... tls [tlsSlots]uintptr // thread-local storage (for x86 extern register) ... }
从上面可知DI保存的是m.tls 的首地址,然后在settls中加上了8字节也就是8x8=64位也就是 m.tls[1]地址用于设置TLS。
// set tls base to DI TEXT runtime·settls(SB),NOSPLIT,$32 ADDQ $8, DI // ELF wants to use -8(FS) 将DI寄存器的值加上8字节,得到TLS的偏移量。这个偏移量用于设置TLS。 MOVQ DI, SI // 将TLS地址放到SI寄存器,也就是系统调用第二个参数 MOVQ $0x1002, DI // ARCH_SET_FS 设置系统调用号 MOVQ $SYS_arch_prctl, AX // 执行系统调用,将tls 设置到 m.tls[1] 上 SYSCALL CMPQ AX, $0xfffffffffffff001 JLS 2(PC) MOVL $0xf1, 0xf1 // crash RET
2、gettls
TEXT runtime·rt0_go(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 .... // store through it, to make sure it works get_tls(BX) MOVQ $0x123, g(BX) MOVQ runtime·m0+m_tls(SB), AX CMPQ AX, $0x123 JEQ 2(PC) CALL runtime·abort(SB) ok: // set the per-goroutine and per-mach "registers" get_tls(BX) // m.tls 地址也就是m.tls[0] LEAQ runtime·g0(SB), CX // 将g0 放入CX MOVQ CX, g(BX) // 将CX也就是 g0 放入BX 也就是m.tls[0] = &g0 ....
3、gogo函数
go中在excute函数中执行一个goroutine,最后调用gogo函数执行goroutine,可以看到runtime.gogo 中将gogo函数的第一个参数放到了BX寄存器,
将gobuf.g也就是当前goroutine放到了DX寄存器,然后在TEXT gogo 中通过get_tls 获取到当前线程存储的地址放到CX,然后将DX寄存器也就是当前groutine放到当前线程存储中,同步将DX也就是当前goroutine放到R14寄存器(会在栈扩容检查中使用)
后面调度代码不在赘述,总结就是将当前goroutine的 pc 放到BX寄存器,跳到BX也就是pc处开始执行指令。
go/src/runtime/asm_amd64.s:403
// func gogo(buf *gobuf) // restore state from Gobuf; longjmp TEXT runtime·gogo(SB), NOSPLIT, $0-8 MOVQ buf+0(FP), BX // gobuf MOVQ gobuf_g(BX), DX // 将要执行的goroutine放到 DX MOVQ 0(DX), CX // make sure g != nil JMP gogo<>(SB) TEXT gogo<>(SB), NOSPLIT, $0 get_tls(CX) // 将当前线程存储的地址放到 CX MOVQ DX, g(CX) // 将DX也就是要执行的goroutine放到 CX,也就是 m.tls[0] MOVQ DX, R14 // set the g register 将要执行的goroutine 放到R14寄存器 MOVQ gobuf_sp(BX), SP // restore SP MOVQ gobuf_ret(BX), AX MOVQ gobuf_ctxt(BX), DX MOVQ gobuf_bp(BX), BP MOVQ $0, gobuf_sp(BX) // clear to help garbage collector MOVQ $0, gobuf_ret(BX) MOVQ $0, gobuf_ctxt(BX) MOVQ $0, gobuf_bp(BX) MOVQ gobuf_pc(BX), BX // 将当前goroutine的 pc 放到BX寄存器 JMP BX // 跳到BX也就是pc处开始执行指令
4、栈检查
> gogo() /usr/local/go/src/runtime/asm_amd64.s:422 (PC: 0x45a77e) Warning: debugging optimized function ... asm_amd64.s:421 0x45a77a* 488b5b08 mov rbx, qword ptr [rbx+0x8] => asm_amd64.s:422 0x45a77e ffe3 jmp rbx // 跳转到pc 此处也就是main函数执行 (dlv) regs Rip = 0x000000000045a77e Rsp = 0x000000c00004a7d8 Rax = 0x0000000000000000 Rbx = 0x0000000000433180 // 保存了main函数的地址 ... (dlv) si > runtime.main() /usr/local/go/src/runtime/proc.go:145 (PC: 0x433180) Warning: debugging optimized function TEXT runtime.main(SB) /usr/local/go/src/runtime/proc.go // 扩栈检查,比较rsp 与 r14 也就是当前g 偏移0x10 也就是16个字节中的 stackguard0 比较 => proc.go:145 0x433180 493b6610 cmp rsp, qword ptr [r14+0x10] proc.go:145 0x433184 0f861c030000 jbe 0x4334a6 proc.go:145 0x43318a 4883ec58 sub rsp, 0x58 ... proc.go:145 0x4334a6 e855820200 call $runtime.morestack_noctxt // 执行扩栈 proc.go:145 0x4334ab e9d0fcffff jmp $runtime.main // 扩栈完成,跳转回main执行
g定义
type g struct {
stack stack // offset known to runtime/cgo 16个字节
stackguard0 uintptr // offset known to liblink 8字节
...
}
// Stack describes a Go execution stack.
// The bounds of the stack are exactly [lo, hi),
// with no implicit data structures on either side.
type stack struct {
lo uintptr // 8字节
hi uintptr // 8字节
}
人生还有意义。那一定是还在找存在的理由