上卷 程序设计
中卷 标准库
- 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 执行。
系统使用汇编(asm_amd64.s)实现了一些系统核心调用指令。
有关汇编语法相关内容,参考《语言规范,进阶 - 混合编程》。
正常情况下,不会调整 g0.sched 状态。
总是让其保持 mstart 为初始栈帧状态,然后以此开始执行。
所以,每次调用都丛第二栈帧开始,无须清理调用堆栈。
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(SB), NOSPLIT, $0-8 MOVQ fn+0(FP), DI // 将参数 fn 保存到 DI。 get_tls(CX) MOVQ g(CX), AX // 当前 G 保存到 AX。 MOVQ 0(SP), BX // mcall 栈帧大小为 0,0(SP) 指向 caller IP/PC。 MOVQ BX, (g_sched+gobuf_pc)(AX) // 保存 caller PC 到 sched.pc。 LEAQ fn+0(FP), BX // FP 指向第一个参数,那么 0(FP)自然是 caller SP。 MOVQ BX, (g_sched+gobuf_sp)(AX) // 保存 caller.sp 等相关状态。(宏函数) MOVQ AX, (g_sched+gobuf_g)(AX) MOVQ BP, (g_sched+gobuf_bp)(AX) // switch to m->g0 & its stack, call fn MOVQ g(CX), BX MOVQ g_m(BX), BX MOVQ m_g0(BX), SI // 将 M.g0 保存到 SI。 CMPQ SI, AX // if g == m->g0 call badmcall JNE 3(PC) MOVQ $runtime·badmcall(SB), AX JMP AX MOVQ SI, g(CX) // g = m->g0 MOVQ (g_sched+gobuf_sp)(SI), SP // sp = m->g0->sched.sp。设置 SP,恢复 g0 栈。 PUSHQ AX // 将原 G 入栈,做为 fn 函数的调用参数。 MOVQ DI, DX // 将 fn 放入 DX。 MOVQ 0(DI), DI CALL DI // 调用 fn。 POPQ AX MOVQ $runtime·badmcall2(SB), AX JMP AX RET
systemstack
切换到 M.g0 执行特定函数,然后恢复 G 执行。
// func systemstack(fn func()) TEXT runtime·systemstack(SB), NOSPLIT, $0-8 MOVQ fn+0(FP), DI // 保存参数 fn 到 DI。 get_tls(CX) MOVQ g(CX), AX // AX = g MOVQ g_m(AX), BX // BX = m CMPQ AX, m_gsignal(BX) JEQ noswitch MOVQ m_g0(BX), DX // DX = g0 CMPQ AX, DX JEQ noswitch CMPQ AX, m_curg(BX) JNE bad // switch stacks // save our state in g->sched. Pretend to // be systemstack_switch if the G stack is scanned. MOVQ $runtime·systemstack_switch(SB), SI MOVQ SI, (g_sched+gobuf_pc)(AX) // 保存 G 状态。 MOVQ SP, (g_sched+gobuf_sp)(AX) MOVQ AX, (g_sched+gobuf_g)(AX) MOVQ BP, (g_sched+gobuf_bp)(AX) // switch to g0 MOVQ DX, g(CX) MOVQ (g_sched+gobuf_sp)(DX), BX // make it look like mstart called systemstack on g0, to stop traceback SUBQ $8, BX MOVQ $runtime·mstart(SB), DX MOVQ DX, 0(BX) MOVQ BX, SP // call target function MOVQ DI, DX MOVQ 0(DI), DI CALL DI // switch back to g get_tls(CX) MOVQ g(CX), AX MOVQ g_m(AX), BX MOVQ m_curg(BX), AX MOVQ AX, g(CX) MOVQ (g_sched+gobuf_sp)(AX), SP MOVQ $0, (g_sched+gobuf_sp)(AX) RET
gogo
跳转到指定的 G.sched.pc 执行。
切换到 M.g0 时,mcall、systemstack 都会更新 G.sched 状态。
仅 goexit0 出错时,才执行
gogo(g0.sched)
。正常调用gogo(G.sched)
不会影响 g0 状态。
// func gogo(buf *gobuf) // restore state from Gobuf; longjmp TEXT runtime·gogo(SB), NOSPLIT, $16-8 MOVQ buf+0(FP), BX // gobuf MOVQ gobuf_g(BX), DX // g MOVQ 0(DX), CX // make sure g != nil get_tls(CX) MOVQ DX, g(CX) // 切换到 G。 MOVQ gobuf_sp(BX), SP // 恢复 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 // 跳转执行 G.fn。 JMP BX // 该指令不会 PUSH PC/IP,依然是 goexit。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论