You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

关于runtime.gopanic中g._defer.fn无法影响g._defer的疑问

关于Go 1.20.1中runtime.gopanic代码逻辑的疑问

我对Go 1.20.1版本里runtime.gopanic的这段代码(对应panic.go第890-897行)存在疑问。

测试代码及编译结果

测试Go代码

package main

func main() {
    a := func() {
        b := func() {
            recover()
        }
        defer b()
    }
    defer a()
    panic(1)
}

plan9编译结果

main.go:3       0x105a100       493b6610        cmp rsp, qword ptr [r14+0x10]
        main.go:3       0x105a104       7651            jbe 0x105a157
=>      main.go:3       0x105a106*      4883ec68        sub rsp, 0x68
        main.go:3       0x105a10a       48896c2460      mov qword ptr [rsp+0x60], rbp
        main.go:3       0x105a10f       488d6c2460      lea rbp, ptr [rsp+0x60]
        main.go:4       0x105a114       488d0dc50e0100  lea rcx, ptr [rip+0x10ec5]
        main.go:4       0x105a11b       48894c2458      mov qword ptr [rsp+0x58], rcx
        main.go:10      0x105a120       48894c2428      mov qword ptr [rsp+0x28], rcx
        main.go:10      0x105a125       488d442410      lea rax, ptr [rsp+0x10]
        main.go:10      0x105a12a       e89136fdff      call $runtime.deferprocStack
        main.go:10      0x105a12f       85c0            test eax, eax
        main.go:10      0x105a131       7515            jnz 0x105a148
        main.go:10      0x105a133       eb00            jmp 0x105a135
        main.go:11      0x105a135       488d05043f0000  lea rax, ptr [rip+0x3f04]
        main.go:11      0x105a13c       488d1d8d3a0100  lea rbx, ptr [rip+0x13a8d]
        main.go:11      0x105a143       e83847fdff      call $runtime.gopanic
        main.go:10      0x105a148       e8533cfdff      call $runtime.deferreturn
        main.go:10      0x105a14d       488b6c2460      mov rbp, qword ptr [rsp+0x60]
        main.go:10      0x105a152       4883c468        add rsp, 0x68
        main.go:10      0x105a156       c3              ret
        main.go:3       0x105a157       e8c4cfffff      call $runtime.morestack_noctxt
        .:0             0x105a15c       eba2            jmp $main.main

问题分析

在Go中,defer会调用runtime.deferprocStack,让当前g._defer指向最内层的defer;panic会调用runtime.gopanic。

我的测试代码里,调用runtime.gopanic时,当前g._defer的结构如下:

g._defer = &_defer{ fn: `main.a`, link: nil }

因此在panic.go第890行处会执行main.a(),而main.a内部会创建一个fn为main.a.b的新_defer,此时g._defer会指向这个新defer,原defer会被赋值为它的link,结构变为:

g._defer = &_defer{ fn: `main.a.b`, link: &_defer{ fn: `main.a`, link: nil } }

但实际调试中,panic.go第895行的gp._defer != d依然成立。我是不是忽略了整个过程里的某些细节?

调试环境

  • Go版本:1.20.1
  • 机器:64位macOS 11.7.2
  • 内核版本:Darwin 20.6.0
  • 调试工具:dlv

附录:调试截图

dlv调试截图1
dlv调试截图2

会不会是执行runtime.gopanic和runtime.deferprocStack的不是同一个goroutine?

内容的提问来源于stack exchange,提问作者spin

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.29 02:17:51