go中的栈切换疑问

不是太理解golang关于协程栈切换的问题
  • runtime.asmcgocall是用汇编实现的,它会切换到m的g0栈,然后调用_cgo_Cfunc_test函数。由于m的g0栈不是分段栈,因此切换到m->g0栈(这个栈是操作系统分配的栈)后,可以安全地运行gcc编译的代码以及执行_cgo_Cfunc_test(frame)函数。
  • 问题

    • 1.从用户协程切换到m.g0协程的栈,之后的操作都在m.g0内继续运行。只是切换了栈,为什么m.g0会去运行,不理解为什么只是切换栈而不是执行调度会让m.g0运行?
    • 2.如果m.g0中已有运行的逻辑,切换到m.g0后不会有问题吗?
    • 以下是golang源码中关于cgo的汇编部分,查了很多资料之说切换到m.g0(共用系统的线程栈),本人对此缺乏知识,望同道解答一二。
// func asmcgocall(fn, arg unsafe.Pointer) int32
// Call fn(arg) on the scheduler stack,
// aligned appropriately for the gcc ABI.
// See cgocall.go for more details.
TEXT ·asmcgocall(SB),NOSPLIT,$0-20
    MOVQ    fn+0(FP), AX
    MOVQ    arg+8(FP), BX

    MOVQ    SP, DX

    // Figure out if we need to switch to m->g0 stack.
    // We get called to create new OS threads too, and those
    // come in on the m->g0 stack already.
    get_tls(CX)
    MOVQ    g(CX), R8
    CMPQ    R8, $0
    JEQ    nosave
    MOVQ    g_m(R8), R8
    MOVQ    m_g0(R8), SI
    MOVQ    g(CX), DI
    CMPQ    SI, DI
    JEQ    nosave
    MOVQ    m_gsignal(R8), SI
    CMPQ    SI, DI
    JEQ    nosave

    // Switch to system stack.
    MOVQ    m_g0(R8), SI
    CALL    gosave<>(SB)
    MOVQ    SI, g(CX)
    MOVQ    (g_sched+gobuf_sp)(SI), SP

    // Now on a scheduling stack (a pthread-created stack).
    // Make sure we have enough room for 4 stack-backed fast-call
    // registers as per windows amd64 calling convention.
    SUBQ    $64, SP
    ANDQ    $~15, SP    // alignment for gcc ABI
    MOVQ    DI, 48(SP)    // save g
    MOVQ    (g_stack+stack_hi)(DI), DI
    SUBQ    DX, DI
    MOVQ    DI, 40(SP)    // save depth in stack (can't just save SP, as stack might be copied during a callback)
    MOVQ    BX, DI        // DI = first argument in AMD64 ABI
    MOVQ    BX, CX        // CX = first argument in Win64
    CALL    AX

    // Restore registers, g, stack pointer.
    get_tls(CX)
    MOVQ    48(SP), DI
    MOVQ    (g_stack+stack_hi)(DI), SI
    SUBQ    40(SP), SI
    MOVQ    DI, g(CX)
    MOVQ    SI, SP

    MOVL    AX, ret+16(FP)
    RET

nosave:
    // Running on a system stack, perhaps even without a g.
    // Having no g can happen during thread creation or thread teardown
    // (see needm/dropm on Solaris, for example).
    // This code is like the above sequence but without saving/restoring g
    // and without worrying about the stack moving out from under us
    // (because we're on a system stack, not a goroutine stack).
    // The above code could be used directly if already on a system stack,
    // but then the only path through this code would be a rare case on Solaris.
    // Using this code for all "already on system stack" calls exercises it more,
    // which should help keep it correct.
    SUBQ    $64, SP
    ANDQ    $~15, SP
    MOVQ    $0, 48(SP)        // where above code stores g, in case someone looks during debugging
    MOVQ    DX, 40(SP)    // save original stack pointer
    MOVQ    BX, DI        // DI = first argument in AMD64 ABI
    MOVQ    BX, CX        // CX = first argument in Win64
    CALL    AX
    MOVQ    40(SP), SI    // restore original stack pointer
    MOVQ    SI, SP
    MOVL    AX, ret+16(FP)
    RET
阅读 1.9k
撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题