返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.5.4 内核调用

发布于 2024-10-12 19:16:06 字数 4504 浏览 0 评论 0 收藏 0

运行时命令不能使用用户栈(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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文