上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4.5.4 内核调用
运行时不能使用用户栈(G.stack),须切换到 M.g0 执行。为此用汇编实现了专门的调用函数。
有关汇编语法相关内容,参考《语言规范,进阶 - 混合编程》。
正常情况下,不会调整 g0.sched 设置。保持 mstart1 设置的初始状态。以此开始执行,复用栈内存。
因此,每次切换到 g0 都丛第二栈帧开始,无须清理调用堆栈。一旦出错,就回到 mstart0 内调用 mexit 终止。
mcall
保存当前 G 执行状态,切换到 g0 执行特定函数。由于函数不返回,所以 mcall 不负责切换回 G。
// asm_amd64.s // func mcall(fn func(*g)) // Switch to m->g0's stack, call fn(g). // Fn must never return. It should gogo(&g->sched) // to keep running g. TEXT runtime·mcall<ABIInternal>(SB), NOSPLIT, $0-8 // 参数 MOVQ AX, DX // DX = fn // 保存状态(g.sched)。 MOVQ 0(SP), BX MOVQ BX, (g_sched+gobuf_pc)(R14) LEAQ fn+0(FP), BX MOVQ BX, (g_sched+gobuf_sp)(R14) MOVQ BP, (g_sched+gobuf_bp)(R14) MOVQ g_m(R14), BX // BX = g.m MOVQ m_g0(BX), SI // SI = g.m.g0 CMPQ SI, R14 // if g == m->g0 call badmcall JNE goodm JMP runtime·badmcall(SB) goodm: // 保存当前 g,作为后续调用参数。 MOVQ R14, AX // AX (and arg 0) = g MOVQ SI, R14 // g.m.g0 // 恢复 g0 栈。 get_tls(CX) // Set G in TLS MOVQ R14, g(CX) MOVQ (g_sched+gobuf_sp)(R14), SP // sp = g0.sched.sp // 调用目标函数。 PUSHQ AX // fn.arg = g MOVQ 0(DX), R12 CALL R12 // fn(g) POPQ AX JMP runtime·badmcall2(SB) RET
提示:get_tls 将 G 指针存入指定寄存器。
systemstack
切换到 M.g0 执行特定函数,然后恢复 G 执行。
// asm_amd64.s // func systemstack(fn func()) TEXT runtime·systemstack(SB), NOSPLIT, $0-8 // 参数。 MOVQ fn+0(FP), DI // DI = fn // 当前 G、M。 get_tls(CX) MOVQ g(CX), AX // AX = g MOVQ g_m(AX), BX // BX = m CMPQ AX, m_gsignal(BX) JEQ noswitch // 获取 M.g0。 MOVQ m_g0(BX), DX // DX = g0 CMPQ AX, DX JEQ noswitch CMPQ AX, m_curg(BX) JNE bad // 保存当前 g 状态(g.sched)。 CALL gosave_systemstack_switch<>(SB) // 切换到 g0 栈。 MOVQ DX, g(CX) MOVQ DX, R14 // set the g register MOVQ (g_sched+gobuf_sp)(DX), BX MOVQ BX, SP // 调用目标函数。 MOVQ DI, DX MOVQ 0(DI), DI CALL DI // 切换回 g。 get_tls(CX) MOVQ g(CX), AX MOVQ g_m(AX), BX // M MOVQ m_curg(BX), AX // M.curg !!! MOVQ AX, g(CX) MOVQ (g_sched+gobuf_sp)(AX), SP MOVQ $0, (g_sched+gobuf_sp)(AX) RET
// Save state of caller into g->sched, TEXT gosave_systemstack_switch<>(SB),NOSPLIT,$0 MOVQ $runtime·systemstack_switch(SB), R9 MOVQ R9, (g_sched+gobuf_pc)(R14) LEAQ 8(SP), R9 MOVQ R9, (g_sched+gobuf_sp)(R14) MOVQ $0, (g_sched+gobuf_ret)(R14) MOVQ BP, (g_sched+gobuf_bp)(R14) // Assert ctxt is zero. See func save. MOVQ (g_sched+gobuf_ctxt)(R14), R9 TESTQ R9, R9 JZ 2(PC) CALL runtime·abort(SB) RET
gogo
跳转到指定的 G.sched.pc 执行。
因 gogo 由 schedule/g0 调用,无需保存 G.stack 状态。
仅 goexit0 出错时,才执行 gogo(g0.sched)。
// asm_amd64.s // 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 // gobuf.g MOVQ 0(DX), CX // make sure g != nil JMP gogo<>(SB) TEXT gogo<>(SB), NOSPLIT, $0 get_tls(CX) MOVQ DX, g(CX) MOVQ DX, R14 // set the g register // 切换到 G.stack。 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) // 跳转执行 G.fn。 // 不会保存 gogo.PC,所以 G.fn RET 指向 goexit。 MOVQ gobuf_pc(BX), BX JMP BX
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论