Go1.25的源码分析-src/runtime/runtime1.go(GMP)

1. 主要组成部分

Go语言的GMP调度器基于四个核心数据结构:gmpschedt

1.1 主要常量解读

1.1.1G 状态常量

复制代码
const (
    _Gidle = iota //刚分配尚未初始化的 G
    _Grunnable//已在运行队列上,未执行用户代码;栈未被该 G 拥有
    _Grunning//正在执行用户代码;已分配 M 与 P;不在运行队列上;栈由该 G 拥有
    _Gsyscall//正在执行系统调用;不执行用户代码;不在运行队列上;已分配 M;栈由该 G 拥有
    _Gwaiting//在 runtime 内部阻塞(如通道、锁、定时器等);不执行用户代码;不在运行队列上;通常不拥有栈(特定通道路径在锁下可读写)
    _Gmoribund_unused//预留给调试器(gdb)
    _Gdead//当前未使用的 G(刚退出、在空闲链表、或初始化中);不执行用户代码;可能有或没有栈;由处理其回收/复用的 M 暂时拥有
    _Genqueue_unused
    _Gcopystack//正在进行栈移动/拷贝;不执行用户代码;不在运行队列上;栈由发起拷贝的一方拥有
    _Gpreempted//因 suspendG 抢占而自停,类似 _Gwaiting,但尚无人负责将其 ready;需要某个 suspendG 将状态 CAS 为 _Gwaiting 并负责唤醒
    //GC 扫描期叠加位,用于标注 goroutine 栈扫描/自扫描期间的状态,除了_Gscanrunning
    _Gscan          = 0x1000
    _Gscanrunnable  = _Gscan + _Grunnable
    _Gscanrunning   = _Gscan + _Grunning
    _Gscansyscall   = _Gscan + _Gsyscall
    _Gscanwaiting   = _Gscan + _Gwaiting
    _Gscanpreempted = _Gscan + _Gpreempted
)

1.1.2 P的状态常量

复制代码
const (
	// P status

	_Pidle = iota // 空闲:未用于运行用户代码或调度器,通常在空闲队列

	_Prunning  // 运行中:被某个 M 持有,用于运行用户代码或调度器

	_Psyscall  // 系统调用关联:不运行用户代码,与系统调用中的 M 有亲和性,可被其他 M 窃取

	_Pgcstop   // GC 停止:STW 期间暂停,由发起 STW 的 M 持有

	_Pdead     // 已死亡:GOMAXPROCS 变小后不再使用,资源基本被剥离
)

1.2 重要数据结构

1.2.1 gobuf的结构体

复制代码
// 源码位置:go/src/runtime/runtime2.go
type gobuf struct {
	// sp, pc, g的偏移量是已知的(硬编码在)libmach中
	//
	// ctxt在GC方面是不寻常的:它可能是堆分配的funcval,
	// 所以GC需要跟踪它,但它需要从汇编中设置和清除,
	// 在那里很难有写屏障。然而,ctxt实际上是一个保存的活动寄存器,
	// 我们只在真实寄存器和gobuf之间交换它。
	// 因此,我们在栈扫描期间将其视为根,这意味着保存和恢复它的汇编不需要写屏障
	// 它仍然被类型化为指针,以便Go的任何其他写入都获得写屏障
	sp   uintptr        // 栈指针
	pc   uintptr        // 程序计数器
	g    guintptr       // goroutine指针(绕过写屏障)
	ctxt unsafe.Pointer // 上下文指针,通常指向funcval
	lr   uintptr        // 链接寄存器(用于某些架构)
	bp   uintptr        // 基指针(用于启用帧指针的架构)
    //ret  uintptr     // 1.24.4被删除 返回值 现在直接通过寄存器或 goroutine 的栈传递返回值
}

主要作用:

gobuf是运行时"协程上下文快照"的结构;调度、栈切换、cgo 边界、栈扩容等一切需要"暂停-继续"的地方,都会写/读它;真正的保存和跳转由 gosave/gogo 这类汇编原语完成。

1.2.2 G的结构体

复制代码
type g struct {
    // Stack parameters.
    // stack 描述实际栈内存区间: [stack.lo, stack.hi)。
    // stackguard0 是 Go 栈增长前序中比较的栈指针。
    //   通常为 stack.lo+StackGuard,但可设为 StackPreempt 以触发抢占。
    // stackguard1 是 //go:systemstack 栈增长前序中比较的栈指针。
    //   在 g0 和 gsignal 栈上为 stack.lo+StackGuard;
    //   在其他 goroutine 栈上为 ~0,以触发 morestackc 并导致崩溃。
    stack       stack   // 偏移量由 runtime/cgo 已知
    stackguard0 uintptr // 偏移量由 liblink 已知
    stackguard1 uintptr // 偏移量由 liblink 已知

    _panic    *_panic // 最内层 panic,偏移量由 liblink 已知
    _defer    *_defer // 最内层 defer
    m         *m      // 当前绑定的 M,偏移量由 arm liblink 已知
    sched     gobuf   // 调度上下文(保存寄存器、PC、SP 等)
    // 系统调用相关上下文(用于 GC 时保留)
    syscallsp uintptr // 若 status==Gsyscall,此处存 sched.sp
    syscallpc uintptr // 若 status==Gsyscall,此处存 sched.pc
    syscallbp uintptr // 若 status==Gsyscall,此处存 sched.bp 用于回溯
    stktopsp  uintptr // 回溯时期望的栈顶 SP

    // param 是通用指针参数字段,用于在特定场景传递值:
    // 1. channel 操作唤醒阻塞 goroutine 时,指向该次阻塞的 sudog;
    // 2. gcAssistAlloc1 通知调用者完成 GC 周期(因栈可能已搬迁);
    // 3. debugCallWrap 传参给新 goroutine(运行时禁止闭包分配);
    // 4. panic 恢复返回到某帧时,指向 savedOpenDeferState。
    param        unsafe.Pointer
    atomicstatus atomic.Uint32 // 原子状态
    stackLock    uint32        // sigprof/scang 锁;TODO: 合并到 atomicstatus
    goid         uint64        // goroutine 唯一 ID
    schedlink    guintptr      // 全局可运行队列链接
    waitsince    int64         // 估计阻塞开始时间
    waitreason   waitReason    // 若 status==Gwaiting,记录原因

    // 抢占控制
    preempt       bool // 抢占信号(等价于 stackguard0 = stackpreempt)
    preemptStop   bool // 抢占时转入 _Gpreempted,否则仅去调度
    preemptShrink bool // 在同步安全点收缩栈

    // asyncSafePoint 表示 g 在异步安全点停下,此时栈上可能无精确指针信息
    asyncSafePoint bool

    // paniconfault 在意外故障地址时 panic 而非直接 crash
    paniconfault bool
    // gcscandone 表示栈扫描已完成,受 status 的 _Gscan 位保护
    gcscandone bool
    // throwsplit 禁止在此 g 上进行栈分裂
    throwsplit bool
    // activeStackChans 表示未加锁 channel 引用此栈,收缩时需加锁保护
    activeStackChans bool
    // parkingOnChan 表示即将 park 在 chansend/chanrecv 上,标记栈收缩不安全点
    parkingOnChan atomic.Bool
    // inMarkAssist 表示是否在执行 mark assist(执行跟踪使用)
    inMarkAssist bool
    coroexit     bool // coroswitch_m 的参数

    // raceignore 忽略竞态检测事件
    raceignore    int8  // 忽略竞态检测事件
    nocgocallback bool  // 禁止从 C 回调到 Go
    tracking      bool  // 是否跟踪此 G 的调度延迟统计
    trackingSeq   uint8 // 决定是否跟踪的序列号
    trackingStamp int64 // 开始跟踪的时间戳
    runnableTime  int64 // 可运行时间累计(运行时清零),仅在跟踪时使用

    lockedm         muintptr      // LockThread 时锁定的 M
    fipsIndicator   uint8         // FIPS 模式指示
    syncSafePoint   bool          // 是否停在同步安全点
    runningCleanups atomic.Bool   // 是否正在运行清理函数
    sig             uint32        // 信号编号
    writebuf       byte        // 信号处理写缓冲区
    sigcode0        uintptr       // 信号处理相关寄存器
    sigcode1        uintptr
    sigpc           uintptr

    parentGoid uint64          // 创建此 goroutine 的父 goid
    gopc       uintptr         // 创建此 goroutine 的 go 语句的 PC
    ancestors  *ancestorInfo // debug.tracebackancestors 模式下的祖先链
    startpc    uintptr         // goroutine 函数入口 PC
    racectx    uintptr         // 竞态检测上下文

    // waiting 指向当前 g 等待的 sudog 链表(elem 有效)
    waiting *sudog

    cgoCtxtuintptr      // cgo 回溯上下文
    labels  unsafe.Pointer // 性能分析器标签
    timer   *timer         // time.Sleep 缓存的定时器
    sleepWhen int64        // 睡眠到期时间
    selectDone atomic.Uint32 // 是否参与 select 及是否赢得唤醒竞赛

    // goroutineProfiled 表示当前 goroutine 栈在 profile 中的状态
    goroutineProfiled goroutineProfileStateHolder

    coroarg *coro             // 协程切换参数
    bubble  *synctestBubble   // 同步测试气泡

    // Per-G 追踪状态
    trace gTraceState

    // GC 相关状态
    // gcAssistBytes 为 GC 辅助信用额度(字节数):
    //   >0 表示有可用额度;
    //   <0 表示需完成扫描工作。
    // 通过 assist ratio 转换为扫描工作债务。
    gcAssistBytes int64

    // valgrindStackID 在 valgrind build tag 下用于跟踪栈内存,否则未使用
    valgrindStackID uintptr
}
G的状态转换图

1.2.3 M的结构体

复制代码
type m struct {
    g0    *g// g0: 持有调度堆栈的goroutine
    morebuf    gobuf// morebuf: 传递给morestack的gobuf参数
    divmod    uint32// divmod: ARM平台的除法/模运算分母(已知liblink,详见cmd/internal/obj/arm/obj5.go)

    // 调试器未知的字段
    procid uint64 // 供调试器使用,但偏移量未硬编码
    gsignal *g // 信号处理专用的goroutine
    goSigStack gsignalStack // Go分配的信号处理堆栈
    sigmask sigset // 保存的信号掩码存储
    tls [tlsSlots]uintptr // 线程局部存储(用于x86外部寄存器)
    mstartfn func() // M启动函数
    curg *g // 当前运行的goroutine
    caughtsig guintptr // 在致命信号期间运行的goroutine
    p puintptr // 附加的P(用于执行Go代码,未执行Go代码时为nil)
    nextp puintptr // 下一个P
    oldp puintptr // 执行系统调用前附加的P
    id int64 // M的唯一ID
    mallocing int32 // 是否在分配内存
    throwing throwType // 当前抛出类型
    preemptoff string // 若非空,强制保持curg在此M上运行
    locks int32 // 保持锁定的次数
    dying int32 // 死亡状态标志
    profilehz int32 // 性能分析频率
    spinning bool // M处于空闲状态并主动寻找工作
    blocked bool // M被note阻塞
    newSigstack bool // minit在C线程中调用了sigaltstack
    printlock int8 // 打印锁
    incgo bool // 是否在执行cgo调用
    isextra bool // 是否为备用M
    isExtraInC bool // 是否为在C代码中运行的备用M
    isExtraInSig bool // 是否为在信号处理中运行的备用M
    freeWait atomic.Uint32 // 是否可以安全释放g0并删除M(freeMRef/freeMStack/freeMWait之一)
    needextram bool // 是否需要备用M
    g0StackAccurate bool // g0堆栈是否具有准确边界
    traceback uint8 // 回溯类型
    allpSnapshot []*p // 附加P时的allp快照(findRunnable释放P后使用,否则为nil)
    ncgocall uint64 // 总cgo调用次数
    ncgo int32 // 当前进行的cgo调用次数
    cgoCallersUse atomic.Uint32 // 若非零,cgoCallers临时可用
    cgoCallers *cgoCallers // cgo调用崩溃时的回溯信息
    park note // 用于阻塞M的note
    alllink *m // 在allm链表中的链接
    schedlink muintptr // 调度链表链接
    lockedg guintptr // 锁定的goroutine
    createstack [32]uintptr // 创建此线程的堆栈(用于StackRecord.Stack0,必须对齐)
    lockedExt uint32 // 外部LockOSThread状态追踪
    lockedInt uint32 // 内部lockOSThread状态追踪
    mWaitList mWaitList // 运行时锁等待者列表

    mLockProfile mLockProfile // 与runtime.lock争用相关的字段
    profStackuintptr // 用于内存/阻塞/互斥锁堆栈追踪

    // wait*字段用于从gopark传递参数到park_m(因为低级NOSPLIT函数没有堆栈)
    waitunlockf func(*g, unsafe.Pointer) bool // 解锁函数
    waitlock unsafe.Pointer // 等待的锁
    waitTraceSkip int // 跟踪跳过次数
    waitTraceBlockReason traceBlockReason // 跟踪阻塞原因

    syscalltick uint32 // 系统调用计数
    freelink *m // 在sched.freem链表中的链接
    trace mTraceState // 跟踪状态

    // 这些字段太大,不能放在低级NOSPLIT函数的堆栈中
    libcall libcall // 系统调用参数
    libcallpc uintptr // 用于CPU性能分析
    libcallsp uintptr // 调用堆栈指针
    libcallg guintptr // 当前goroutine
    winsyscall winlibcall // Windows平台的系统调用参数

    vdsoSP uintptr // VDSO调用中的堆栈指针(未调用时为0)
    vdsoPC uintptr // VDSO调用中的程序计数器

    // preemptGen: 记录已完成的抢占信号次数(用于检测抢占失败)
    preemptGen atomic.Uint32

    // 该M是否有待处理的抢占信号
    signalPending atomic.Uint32

    // pcvalue查找缓存
    pcvalueCache pcvalueCache

    dlogPerM // 每M的日志记录

    mOS // 操作系统相关字段

    chacha8 chacha8rand.State // ChaCha8随机数生成器状态
    cheaprand uint64 // 快速随机数生成器

    // 该M持有的最多10把锁(由锁排序代码维护)
    locksHeldLen int // 持有锁数量
    locksHeld [10]heldLockInfo // 持有锁信息数组
}
M的内存大小
复制代码
const mRedZoneSize = (16 << 3) * asanenabledBit // redZoneSize(2048)

type mPadded struct {
	m

	// Size the runtime.m structure so it fits in the 2048-byte size class, and
	// not in the next-smallest (1792-byte) size class. That leaves the 11 low
	// bits of muintptr values available for flags, as required by
	// lock_spinbit.go.
	_ [(1 - goarch.IsWasm) * (2048 - mallocHeaderSize - mRedZoneSize - unsafe.Sizeof(m{}))]byte
}

mRedZoneSize是在启用Ascan时,作为栈溢出检测的区域,大小为128字节。也用于栈扩展部分,当stackstackguard0进入red zone,会触发gorwStack扩展栈。

mPadded 的设计目的是 通过填充字段 确保 m 结构体大小为 2048 字节,从而在 muintptr保留低 11 位用于标志位 。该设计在非 Wasm 平台生效,Wasm 平台因内存模型差异跳过填充。核心优化点在于 标志位复用内存对齐,避免额外内存分配,提升并发性能。

1.2.3 P的结构体

复制代码
type p struct {
    id          int32 // 进程ID
    status      uint32 // 状态(如 pidle/prunning/...)
    link        puintptr // 链表指针
    schedtick   uint32 // 每次调度调用时递增的计数器
    syscalltick uint32 // 每次系统调用时递增的计数器
    sysmontick  sysmontick // sysmon 上次观察到的计数器
    m           muintptr // 关联的 M(空闲时为 nil)
    mcache      *mcache // 本地 M 的缓存
    pcache      pageCache // 页缓存
    raceprocctx uintptr // 竞态检测上下文

    deferpool    []*_defer // 可用 defer 结构体池(见 panic.go)
    deferpoolbuf [32]*_defer // defer 结构体缓冲区

    // 缓存的 goroutine ID,减少对 runtime·sched.goidgen 的访问
    goidcache    uint64 // goroutine ID 缓存起始值
    goidcacheend uint64 // 缓存的 goroutine ID 终止值

    // 可运行的 goroutine 队列(无锁访问)
    runqhead uint32 // 队列头部索引
    runqtail uint32 // 队列尾部索引
    runq     [256]guintptr // 队列数组

    // runnext 存储当前 G 准备运行的下一个 G(若时间片未用尽)。
    // 它继承当前时间片的剩余时间。若一组 goroutine 处于通信和等待模式中,
    // 此字段可将该组作为一个单元调度,避免将 goroutine 添加到队列尾部导致的调度延迟。
    // 注意:其他 P 可以原子地将此字段置为零,但只有当前 P 可以原子地设置为有效 G。
    runnext guintptr

    // 可用的 G(状态 == Gdead)
    gFree gList // 可回收的 G 列表

    sudogcache []*sudog // sudog 缓存
    sudogbuf   [128]*sudog // sudog 缓冲区

    // 从堆中缓存的 mspan 对象
    mspancache struct {
        // 显式长度字段,避免在分配路径中使用写屏障时的复杂性
        len int // 当前缓存长度
        buf [128]*mspan // 缓存数组
    }

    // 缓存的单个 pinner 对象,减少重复创建 pinner 的分配开销
    pinnerCache *pinner

    trace pTraceState // 跟踪状态

    palloc persistentAlloc // 每个 P 的持久化分配器

    // 每个 P 的 GC 状态
    gcAssistTime         int64 // GC 辅助分配所花费的时间(纳秒)
    gcFractionalMarkTime int64 // 分数标记工作者所花费的时间(纳秒,原子更新)

    // limiterEvent 跟踪 GC CPU 限制器的事件
    limiterEvent limiterEvent

    // gcMarkWorkerMode 指示下一个标记工作者应运行的模式
    // 用于与通过 gcController.findRunnableGCWorker 选择的工作者 goroutine 通信
    // 调度其他 goroutine 时,此字段必须设置为 gcMarkWorkerNotWorker
    gcMarkWorkerMode gcMarkWorkerMode
    // gcMarkWorkerStartTime 是最近标记工作者的启动时间(纳秒)
    gcMarkWorkerStartTime int64

    // gcw 是此 P 的 GC 工作缓冲区缓存
    // 缓冲区由写屏障填充,由突变器辅助消耗,在特定 GC 状态转换时释放
    gcw gcWork

    // wbBuf 是此 P 的 GC 写屏障缓冲区
    // TODO: 考虑将其缓存在运行的 G 中
    wbBuf wbBuf

    runSafePointFn uint32 // 若为 1,调度器在下一个安全点运行 sched.safePointFn

    // statsSeq 是指示此 P 是否正在写入统计信息的计数器
    // 偶数表示未写入,奇数表示正在写入
    statsSeq atomic.Uint32

    // 定时器堆
    timers timers

    // 清理块
    cleanups       *cleanupBlock
    cleanupsQueued uint64 // 此 P 队列的清理块数量(单调递增)

    // maxStackScanDelta 累积活动 goroutine 的栈空间占用(即可能需要扫描的栈大小)
    // 当达到 maxStackScanSlack 或 -maxStackScanSlack 时,刷新到 gcController.maxStackScan
    maxStackScanDelta int64

    // GC 时间统计
    // 与 maxStackScan 不同,该字段累积 GC 时实际观察到的栈使用量(hi - sp)
    // 而非瞬时的总栈大小(hi - lo)
    scannedStackSize uint64 // 此 P 扫描的 goroutine 栈大小
    scannedStacks    uint64 // 此 P 扫描的 goroutine 数量

    // preempt 标记此 P 需尽快进入调度器(无论当前 G 在运行什么)
    preempt bool

    // gcStopTime 是此 P 最近进入 _Pgcstop 的时间戳(纳秒)
    gcStopTime int64
}

1.2.4 schedt结构体

复制代码
type schedt struct {
    // goidgen 是全局唯一的 goroutine ID 生成器
    // lastpoll 记录上次网络轮询的时间(若当前正在轮询则为 0)
    // pollUntil 记录当前轮询的睡眠截止时间
    // pollingNet 表示是否有 P 正在执行非阻塞的网络轮询
    goidgen    atomic.Uint64
    lastpoll   atomic.Int64 // 上次网络轮询时间
    pollUntil  atomic.Int64 // 当前轮询的睡眠截止时间
    pollingNet atomic.Int32 // 1 表示有 P 正在执行非阻塞轮询

    // lock 是全局调度器的互斥锁
    lock mutex

    // 增加 nmidle、nmidlelocked、nmsys 或 nmfreed 时,必须调用 checkdead()
    // midle 是等待工作的空闲 M 链表
    // nmidle 是空闲 M 的数量
    // nmidlelocked 是被锁定的空闲 M 数量
    // mnext 是已创建的 M 总数和下一个 M 的 ID
    // maxmcount 是允许的最大 M 数量(超过则终止)
    // nmsys 是系统 M 的数量(不计入死锁检测)
    // nmfreed 是累计释放的 M 数量
    midle        muintptr // 空闲 M 链表
    nmidle       int32    // 空闲 M 数量
    nmidlelocked int32    // 被锁定的空闲 M 数量
    mnext        int64    // 已创建的 M 数量和下一个 M ID
    maxmcount    int32    // 最大允许的 M 数量
    nmsys        int32    // 系统 M 数量
    nmfreed      int64    // 累计释放的 M 数量

    // ngsys 是系统 goroutine 的数量
    ngsys atomic.Int32

    // pidle 是空闲 P 链表
    // npidle 是空闲 P 数量(原子更新)
    // nmspinning 是自旋的 M 数量(参考 proc.go 中的"Worker thread parking/unparking"注释)
    // needspinning 是是否需要自旋的标志(参考 proc.go 中的"Delicate dance"注释,布尔值,修改时需持有 sched.lock)
    pidle        puintptr // 空闲 P 链表
    npidle       atomic.Int32
    nmspinning   atomic.Int32
    needspinning atomic.Uint32

    // 全局可运行队列
    runq gQueue

    // disable 控制调度器的禁用(通过 schedEnableUser 控制)
    // disable.user 表示是否禁用用户 goroutine 的调度
    // disable.runnable 是待运行的 G 队列
    disable struct {
        user     bool
        runnable gQueue // 待运行的 G 队列
    }

    // gFree 是全局死亡 G 的缓存
    gFree struct {
        lock    mutex
        stack   gList // 带栈的 G 列表
        noStack gList // 无栈的 G 列表
    }

    // sudoglock 保护 sudogcache 的互斥锁
    // sudogcache 是全局 sudog 缓存
    sudoglock  mutex
    sudogcache *sudog

    // deferlock 保护 deferpool 的互斥锁
    // deferpool 是全局 defer 结构体缓存
    deferlock mutex
    deferpool *_defer

    // freem 是 m.exited 被设置后等待释放的 M 链表(通过 m.freelink 连接)
    freem *m

    // gcwaiting 表示 GC 是否在等待运行
    // stopwait 和 stopnote 用于 stop-the-world 等待
    // sysmonwait 表示 sysmon 是否在等待
    // sysmonnote 是 sysmon 的等待信号
    gcwaiting  atomic.Bool // GC 等待运行标志
    stopwait   int32
    stopnote   note
    sysmonwait atomic.Bool
    sysmonnote note

    // safePointFn 是在下一个 GC 安全点需要调用的函数(若 p.runSafePointFn 被设置)
    // safePointWait 是等待计数
    // safePointNote 是安全点的等待信号
    safePointFn   func(*p)
    safePointWait int32
    safePointNote note

    // profilehz 是 CPU 采样率
    profilehz int32

    // procresizetime 是上次调整 gomaxprocs 的时间(纳秒)
    // totaltime 是从 procresizetime 开始的累计运行时间
    procresizetime int64 // 上次调整 gomaxprocs 的时间
    totaltime      int64 // 累计运行时间

    // customGOMAXPROCS 表示 GOMAXPROCS 是否被手动设置(环境变量或 runtime.GOMAXPROCS)
    customGOMAXPROCS bool

    // sysmonlock 是保护 sysmon 与运行时交互的互斥锁
    // 持有该锁可阻断 sysmon 对运行时的操作
    sysmonlock mutex

    // timeToRun 是调度延迟分布(定义为 G 在 _Grunnable 状态到 _Grunning 状态的总时间)
    timeToRun timeHistogram

    // idleTime 是所有 P 的空闲时间总和(每次 GC 周期重置)
    idleTime atomic.Int64

    // totalMutexWaitTime 是 goroutine 在 _Gwaiting 状态等待 runtime 内部锁的总时间
    totalMutexWaitTime atomic.Int64

    // stwStoppingTimeGC/Other 是 stop-the-world 停止延迟分布(定义为 stopTheWorldWithSema 到所有 P 停止的时间)
    // stwStoppingTimeGC 覆盖所有 GC 相关的 STW,stwStoppingTimeOther 覆盖其他 STW
    stwStoppingTimeGC    timeHistogram
    stwStoppingTimeOther timeHistogram

    // stwTotalTimeGC/Other 是 stop-the-world 总延迟分布(定义为 stopTheWorldWithSema 到 startTheWorldWithSema 的总时间)
    // stwTotalTimeGC 覆盖所有 GC 相关的 STW,stwTotalTimeOther 覆盖其他 STW
    stwTotalTimeGC    timeHistogram
    stwTotalTimeOther timeHistogram

    // totalRuntimeLockWaitTime(加上每个 M 的 lockWaitTime)是 goroutine 在 _Grunnable 状态且持有 M 但等待 runtime 内部锁的总时间
    // 该字段存储已退出 M 的累计时间
    totalRuntimeLockWaitTime atomic.Int64
}

剩余结构体作用

复制代码
type libcall struct {
    fn uintptr
    n uintptr // 参数个数
    args uintptr // 参数列表
    r1 uintptr // 返回值1
    r2 uintptr // 返回值2
    err uintptr // 错误号
}

// Stack 描述了 Go 运行时的执行栈。
// 栈的边界恰好是 [lo, hi),
// 两端没有任何隐式的数据结构。
type stack struct {
    lo uintptr
    hi uintptr
}

// heldLockInfo 提供已持有的锁及该锁等级的信息
type heldLockInfo struct {
    lockAddr uintptr
    rank lockRank
}

其中libcall在汇编/系统层面,runtime 会构造一个 libcall,把要调用的函数地址、参数列表打包到它的各字段里,然后由通用的调用入口(如 asm/syscall 实现)读取这些字段并真正发起调用,返回值和 errno 也写回到这里。

每个 G 对象里有一个 stack 字段,用 lo、hi 精确标识它的栈内存区域(lo ≤ sp < hi)。GC、栈扩展/收缩、调度等子系统都依赖这两个边界来判断栈是否需要 grow/shrink,以及扫描活跃帧时的地址合法性

Go 运行时为了在调试模式下检测可能的死锁或锁顺序反转,会给每把锁分配一个 rank。每当 G 获取一把锁,就往它的 heldLocks 列表里插入一个 heldLockInfo。释放时再删掉。这样就能在运行时断言"只允许按 rank 升序获取锁",及时报告不安全的锁顺序。

参考文献

https://juejin.cn/post/7519334402688368667#heading-11

https://zhuanlan.zhihu.com/p/67852800

相关推荐
共享家95271 小时前
MYSQL库及表的操作
数据库
阿里云大数据AI技术4 小时前
鹰角网络基于阿里云EMR Serverless StarRocks的实时分析工程实践
数据库·数据分析
久笙&4 小时前
对象存储解决方案:MinIO 的架构与代码实战
数据库·python·架构
码luffyliu4 小时前
MySQL:MVCC机制及其在Java秋招中的高频考点
java·数据库·mysql·事务·并发·mvcc
水涵幽树4 小时前
MySQL 时间筛选避坑指南:为什么格式化字符串比较会出错?
数据库·后端·sql·mysql·database
PythonicCC4 小时前
Django中的MVC和MVT模式
数据库·django·mvc
Mr. Cao code5 小时前
使用Tomcat Clustering和Redis Session Manager实现Session共享
java·linux·运维·redis·缓存·tomcat
AI 嗯啦7 小时前
SQL详细语法教程(七)核心优化
数据库·人工智能·sql
ClouGence8 小时前
三步搞定!GaussDB 实时数据入仓
数据库·后端