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), DICALL 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,$32ADDQ $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] 上SYSCALLCMPQ AX, $0xfffffffffffff001JLS 2(PC)MOVL $0xf1, 0xf1 // crashRET
2、gettls
TEXT runtime·rt0_go(SB),NOSPLIT|NOFRAME|TOPFRAME,$0 ....// store through it, to make sure it worksget_tls(BX)MOVQ $0x123, g(BX)MOVQ runtime·m0+m_tls(SB), AXCMPQ AX, $0x123JEQ 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 放入CXMOVQ 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-8MOVQ buf+0(FP), BX // gobufMOVQ gobuf_g(BX), DXMOVQ 0(DX), CX // make sure g != nilJMP gogo<>(SB)TEXT gogo<>(SB), NOSPLIT, $0get_tls(CX)MOVQ DX, g(CX)MOVQ DX, R14 // set the g registerMOVQ gobuf_sp(BX), SP // restore SPMOVQ gobuf_ret(BX), AXMOVQ gobuf_ctxt(BX), DXMOVQ gobuf_bp(BX), BPMOVQ $0, gobuf_sp(BX) // clear to help garbage collectorMOVQ $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、栈检查