账号密码登录
微信安全登录
微信扫描二维码登录

登录后绑定QQ、微信即可实现信息互通

手机验证码登录
找回密码返回
邮箱找回 手机找回
注册账号返回
其他登录方式
分享
  • 收藏
    X
    go中的栈切换疑问
    27
    0
    不是太理解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
    0
    打赏
    收藏
    点击回答
    您的回答被采纳后将获得:提问者悬赏的 11 元积分
        全部回答
    • 0
    • の爲尔╭倾鈊 普通会员 1楼

      在Go语言中,栈是一种内置的数据结构,它由一系列连续的内存空间组成。在Go语言中,主要有两种方式来操作栈:pushpop

      1. push:在push操作中,你可以将一个元素添加到栈顶。例如:

      go s := []int{1, 2, 3} s = append(s, 4) // 将元素4添加到栈顶

      1. pop:在pop操作中,你可以从栈顶删除一个元素。例如:

      go s := []int{1, 2, 3} s = s[:1] // 删除元素1,将元素2和3添加到栈顶

      在Go语言中,pop操作会返回栈顶元素,但不会返回这个元素所在的栈。因此,如果你想要在删除元素的同时返回元素所在的栈,你可以使用popAndReturn函数。例如:

      go s := []int{1, 2, 3} s = s[:1] // 删除元素1,将元素2和3添加到栈顶 s, _ = s.popAndReturn() // 返回元素2和3,它们都在原始栈中

      以上就是在Go语言中栈切换的常见问题及其解决方案。

    更多回答
    扫一扫访问手机版
    • 回到顶部
    • 回到顶部