【Go 1.26.4】(Part 2) Go 1.26.4 超深度分析 — Runtime GMP 调度器 (proc.go + runtime2.go)

Go 1.26.4 超深度分析 --- Runtime GMP 调度器 (proc.go + runtime2.go)

核心源码:runtime/proc.go (8125行) + runtime/runtime2.go (1519行)

调度器是 Go Runtime 的心脏,负责将就绪的 goroutine 分配到工作线程上执行


一、模块定位

1.1 业务职责

调度器核心职责:将 ready-to-run 的 goroutine 分布到 worker thread 上执行

  • G (Goroutine):用户级轻量线程,携带栈、PC、状态
  • M (Machine):操作系统线程,真正执行代码的载体
  • P (Processor):逻辑处理器,持有本地 runq、mcache、defer pool,是执行 Go 代码的必要资源

关键约束:M 必须关联一个 P 才能执行 Go 代码,但 M 在系统调用或阻塞时可以不持有 P。

1.2 在系统中的位置

#mermaid-svg-8fdIKv9vz7A9PCjs{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-8fdIKv9vz7A9PCjs .error-icon{fill:#552222;}#mermaid-svg-8fdIKv9vz7A9PCjs .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-8fdIKv9vz7A9PCjs .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-8fdIKv9vz7A9PCjs .marker{fill:#333333;stroke:#333333;}#mermaid-svg-8fdIKv9vz7A9PCjs .marker.cross{stroke:#333333;}#mermaid-svg-8fdIKv9vz7A9PCjs svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-8fdIKv9vz7A9PCjs p{margin:0;}#mermaid-svg-8fdIKv9vz7A9PCjs .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs .cluster-label text{fill:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs .cluster-label span{color:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs .cluster-label span p{background-color:transparent;}#mermaid-svg-8fdIKv9vz7A9PCjs .label text,#mermaid-svg-8fdIKv9vz7A9PCjs span{fill:#333;color:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs .node rect,#mermaid-svg-8fdIKv9vz7A9PCjs .node circle,#mermaid-svg-8fdIKv9vz7A9PCjs .node ellipse,#mermaid-svg-8fdIKv9vz7A9PCjs .node polygon,#mermaid-svg-8fdIKv9vz7A9PCjs .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-8fdIKv9vz7A9PCjs .rough-node .label text,#mermaid-svg-8fdIKv9vz7A9PCjs .node .label text,#mermaid-svg-8fdIKv9vz7A9PCjs .image-shape .label,#mermaid-svg-8fdIKv9vz7A9PCjs .icon-shape .label{text-anchor:middle;}#mermaid-svg-8fdIKv9vz7A9PCjs .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-8fdIKv9vz7A9PCjs .rough-node .label,#mermaid-svg-8fdIKv9vz7A9PCjs .node .label,#mermaid-svg-8fdIKv9vz7A9PCjs .image-shape .label,#mermaid-svg-8fdIKv9vz7A9PCjs .icon-shape .label{text-align:center;}#mermaid-svg-8fdIKv9vz7A9PCjs .node.clickable{cursor:pointer;}#mermaid-svg-8fdIKv9vz7A9PCjs .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-8fdIKv9vz7A9PCjs .arrowheadPath{fill:#333333;}#mermaid-svg-8fdIKv9vz7A9PCjs .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-8fdIKv9vz7A9PCjs .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-8fdIKv9vz7A9PCjs .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8fdIKv9vz7A9PCjs .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-8fdIKv9vz7A9PCjs .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8fdIKv9vz7A9PCjs .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-8fdIKv9vz7A9PCjs .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-8fdIKv9vz7A9PCjs .cluster text{fill:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs .cluster span{color:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-8fdIKv9vz7A9PCjs .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-8fdIKv9vz7A9PCjs rect.text{fill:none;stroke-width:0;}#mermaid-svg-8fdIKv9vz7A9PCjs .icon-shape,#mermaid-svg-8fdIKv9vz7A9PCjs .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-8fdIKv9vz7A9PCjs .icon-shape p,#mermaid-svg-8fdIKv9vz7A9PCjs .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-8fdIKv9vz7A9PCjs .icon-shape .label rect,#mermaid-svg-8fdIKv9vz7A9PCjs .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-8fdIKv9vz7A9PCjs .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-8fdIKv9vz7A9PCjs .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-8fdIKv9vz7A9PCjs :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 抢占/retake
唤醒G
STW/GC Worker
用户代码

go f()
newproc() → newproc1()
调度器 (schedule/findRunnable)
G/M/P 管理
sysmon 监控线程
netpoll 网络轮询
GC 调度配合


二、核心结构体逐行解析

2.1 g struct --- Goroutine (runtime2.go:473)

go 复制代码
type g struct {
    // ─── 栈参数 ───
    stack       stack       // 实际栈内存 [stack.lo, stack.hi)
    stackguard0 uintptr     // Go 栈溢出检测 = stack.lo + StackGuard
                            // 可被设为 StackPreempt 触发抢占
    stackguard1 uintptr     // systemstack 栈溢出检测
                            // g0/gsignal = StackGuard; 其他 G = ~0

    // ─── 执行上下文 ───
    _panic    *_panic       // 最内层 panic(链表头)
    _defer    *_defer       // 最内层 defer(链表头)
    m         *m            // 当前关联的 M
    sched     gobuf         // 调度上下文 (sp/pc/bp/g)
    syscallsp uintptr       // _Gsyscall 状态保存 sched.sp
    syscallpc uintptr       // _Gsyscall 状态保存 sched.pc
    syscallbp uintptr       // _Gsyscall 状态保存 sched.bp
    stktopsp  uintptr       // 栈顶期望 sp (traceback校验)

    // ─── 通用参数 ───
    param unsafe.Pointer    // 4种用途: channel唤醒/gcAssist/debugCall/panicRecover

    // ─── 状态与标识 ───
    atomicstatus atomic.Uint32  // G 状态(原子操作)
    stackLock    uint32         // sigprof/scang 锁
    goid         uint64         // goroutine ID
    schedlink    guintptr       // 调度链表下一个 G
    waitsince    int64          // 近似阻塞开始时间
    waitreason   waitReason     // 阻塞原因

    // ─── 抢占控制 ───
    preempt       bool   // 抢占信号
    preemptStop   bool   // 抢占后转为 _Gpreempted
    preemptShrink bool   // 同步安全点时收缩栈
    asyncSafePoint bool  // 异步安全点标记

    // ─── GC 相关 ───
    inMarkAssist  bool    // 是否在 mark assist 中
    gcAssistBytes int64   // GC assist 信用(正=可分配/负=需补偿)

    // ─── 其他 ───
    lockedm     muintptr     // 锁定的 M (LockOSThread)
    timer       *timer       // 缓存 timer (time.Sleep)
    waiting     *sudog       // 等待的 sudog 列表
    parentGoid  uint64       // 创建者 goid
    gopc        uintptr      // go 语句的 PC
    startpc     uintptr      // goroutine 函数入口
    bubble      *synctestBubble  // synctest 气泡
}

2.2 G 状态常量

状态 描述 栈所有权
_Gidle 0 刚分配,未初始化
_Grunnable 1 在 runq 上
_Grunning 2 执行用户代码 G 拥有
_Gsyscall 3 系统调用中 G 拥有
_Gwaiting 4 runtime 阻塞
_Gdead 6 未使用/已退出 M 拥有
_Gcopystack 8 栈复制中
_Gpreempted 9 被抢占等待

#mermaid-svg-01b9wIbp7tg3r16J{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-01b9wIbp7tg3r16J .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-01b9wIbp7tg3r16J .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-01b9wIbp7tg3r16J .error-icon{fill:#552222;}#mermaid-svg-01b9wIbp7tg3r16J .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-01b9wIbp7tg3r16J .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-01b9wIbp7tg3r16J .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-01b9wIbp7tg3r16J .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-01b9wIbp7tg3r16J .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-01b9wIbp7tg3r16J .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-01b9wIbp7tg3r16J .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-01b9wIbp7tg3r16J .marker{fill:#333333;stroke:#333333;}#mermaid-svg-01b9wIbp7tg3r16J .marker.cross{stroke:#333333;}#mermaid-svg-01b9wIbp7tg3r16J svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-01b9wIbp7tg3r16J p{margin:0;}#mermaid-svg-01b9wIbp7tg3r16J defs #statediagram-barbEnd{fill:#333333;stroke:#333333;}#mermaid-svg-01b9wIbp7tg3r16J g.stateGroup text{fill:#9370DB;stroke:none;font-size:10px;}#mermaid-svg-01b9wIbp7tg3r16J g.stateGroup text{fill:#333;stroke:none;font-size:10px;}#mermaid-svg-01b9wIbp7tg3r16J g.stateGroup .state-title{font-weight:bolder;fill:#131300;}#mermaid-svg-01b9wIbp7tg3r16J g.stateGroup rect{fill:#ECECFF;stroke:#9370DB;}#mermaid-svg-01b9wIbp7tg3r16J g.stateGroup line{stroke:#333333;stroke-width:1;}#mermaid-svg-01b9wIbp7tg3r16J .transition{stroke:#333333;stroke-width:1;fill:none;}#mermaid-svg-01b9wIbp7tg3r16J .stateGroup .composit{fill:white;border-bottom:1px;}#mermaid-svg-01b9wIbp7tg3r16J .stateGroup .alt-composit{fill:#e0e0e0;border-bottom:1px;}#mermaid-svg-01b9wIbp7tg3r16J .state-note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-01b9wIbp7tg3r16J .state-note text{fill:black;stroke:none;font-size:10px;}#mermaid-svg-01b9wIbp7tg3r16J .stateLabel .box{stroke:none;stroke-width:0;fill:#ECECFF;opacity:0.5;}#mermaid-svg-01b9wIbp7tg3r16J .edgeLabel .label rect{fill:#ECECFF;opacity:0.5;}#mermaid-svg-01b9wIbp7tg3r16J .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-01b9wIbp7tg3r16J .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-01b9wIbp7tg3r16J .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-01b9wIbp7tg3r16J .edgeLabel .label text{fill:#333;}#mermaid-svg-01b9wIbp7tg3r16J .label div .edgeLabel{color:#333;}#mermaid-svg-01b9wIbp7tg3r16J .stateLabel text{fill:#131300;font-size:10px;font-weight:bold;}#mermaid-svg-01b9wIbp7tg3r16J .node circle.state-start{fill:#333333;stroke:#333333;}#mermaid-svg-01b9wIbp7tg3r16J .node .fork-join{fill:#333333;stroke:#333333;}#mermaid-svg-01b9wIbp7tg3r16J .node circle.state-end{fill:#9370DB;stroke:white;stroke-width:1.5;}#mermaid-svg-01b9wIbp7tg3r16J .end-state-inner{fill:white;stroke-width:1.5;}#mermaid-svg-01b9wIbp7tg3r16J .node rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-01b9wIbp7tg3r16J .node polygon{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-01b9wIbp7tg3r16J #statediagram-barbEnd{fill:#333333;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-cluster rect{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-01b9wIbp7tg3r16J .cluster-label,#mermaid-svg-01b9wIbp7tg3r16J .nodeLabel{color:#131300;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-cluster rect.outer{rx:5px;ry:5px;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-state .divider{stroke:#9370DB;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-state .title-state{rx:5px;ry:5px;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-cluster.statediagram-cluster .inner{fill:white;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-cluster.statediagram-cluster-alt .inner{fill:#f0f0f0;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-cluster .inner{rx:0;ry:0;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-state rect.basic{rx:5px;ry:5px;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-state rect.divider{stroke-dasharray:10,10;fill:#f0f0f0;}#mermaid-svg-01b9wIbp7tg3r16J .note-edge{stroke-dasharray:5;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-note rect{fill:#fff5ad;stroke:#aaaa33;stroke-width:1px;rx:0;ry:0;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-note text{fill:black;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram-note .nodeLabel{color:black;}#mermaid-svg-01b9wIbp7tg3r16J .statediagram .edgeLabel{color:red;}#mermaid-svg-01b9wIbp7tg3r16J #dependencyStart,#mermaid-svg-01b9wIbp7tg3r16J #dependencyEnd{fill:#333333;stroke:#333333;stroke-width:1;}#mermaid-svg-01b9wIbp7tg3r16J .statediagramTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-01b9wIbp7tg3r16J :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 分配
初始化
ready()
execute()
entersyscall()
gopark()
gosched_m()
async preempt
goexit()
exitsyscall()
goready()
调度器选中
_Gidle
_Gdead
_Grunnable
_Grunning
_Gsyscall
_Gwaiting
_Gpreempted

2.3 m struct --- Machine/OS Thread (runtime2.go:618)

关键字段解析:

go 复制代码
type m struct {
    g0      *g     // 调度栈 goroutine (systemstack)
    curg    *g     // 当前运行的用户 goroutine
    p       puintptr  // 当前关联的 P
    nextp   puintptr  // 启动前预装的 P
    oldp    puintptr  // 进入 syscall 前的 P (快速恢复)
    id      int64     // M 唯一 ID
    spinning bool     // 正在自旋寻找工作
    blocked  bool     // 在 note 上阻塞
    locks    int32    // 持有锁计数 (>0 不可抢占)
    incgo    bool     // 正在 cgo 调用
    park     note     // parking note
    lockedg  guintptr // 锁定的 G
    preemptGen atomic.Uint32  // 抢占信号计数
    signalPending atomic.Uint32  // 待处理抢占
}

2.4 p struct --- Processor (runtime2.go:772)

关键字段解析:

go 复制代码
type p struct {
    id          int32        // P ID
    status      uint32       // P 状态
    m           muintptr     // 关联的 M
    mcache      *mcache      // per-P 内存分配缓存(无锁)
    runqhead    uint32       // 本地队列头
    runqtail    uint32       // 本地队列尾
    runq        [256]guintptr // 环形队列(最多256个G)
    runnext     guintptr     // 优先运行的下一个G
    gFree       gList        // 死亡G缓存池
    timers      timers       // 定时器堆
    gcw         gcWork       // GC工作缓冲区
    wbBuf       wbBuf        // 写屏障缓冲区
    goidcache   uint64       // goid缓存(批量分配)
    preempt     bool         // 需进入调度器
}

P 状态:_Pidle(0) / _Prunning(1) / _Psyscall(2) / _Pgcstop(3) / _Pdead(4)

2.5 schedt --- 全局调度器 (runtime2.go:930)

go 复制代码
type schedt struct {
    goidgen    atomic.Uint64   // goroutine ID 生成器
    lock mutex                 // 全局调度锁
    midle     listHeadManual   // 空闲 M 列表
    nmidle    int32            // 空闲 M 数
    pidle     puintptr         // 空闲 P 列表
    npidle    atomic.Int32     // 空闲 P 数
    nmspinning atomic.Int32    // 自旋 M 数
    needspinning atomic.Uint32 // 需要新自旋M
    runq      gQueue           // 全局 runnable 队列
    gcwaiting atomic.Bool      // GC 等待
    sysmonwait atomic.Bool     // sysmon 深度休眠
}

三、核心调度流程

3.1 main() --- 程序入口 (proc.go:149)

go 复制代码
func main() {
    mp := getg().m
    maxstacksize = 1000000000   // 64位: 1GB
    mainStarted = true          // 允许 newproc 启动新M

    // 启动 sysmon 监控线程
    systemstack(func() { newm(sysmon, nil, -1) })

    lockOSThread()    // main goroutine 绑定 m0
    gcenable()        // 启用 GC
    doInit(runtime_inittasks)  // runtime init
    // 执行所有包 init
    for m := &firstmoduledata; true; m = m.next { doInit(m.inittasks) }
    close(main_init_done)
    unlockOSThread()

    fn := main_main   // 调用用户 main.main
    fn()
    exit(0)
}

3.2 newproc → newproc1 --- 创建 Goroutine (proc.go:5295)

wakep() runqput(P本地队列) gfget(P缓存) newproc1() newproc() 用户 go f() wakep() runqput(P本地队列) gfget(P缓存) newproc1() newproc() 用户 go f() #mermaid-svg-mHPPPpseMyagwLbR{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-mHPPPpseMyagwLbR .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-mHPPPpseMyagwLbR .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-mHPPPpseMyagwLbR .error-icon{fill:#552222;}#mermaid-svg-mHPPPpseMyagwLbR .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-mHPPPpseMyagwLbR .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-mHPPPpseMyagwLbR .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-mHPPPpseMyagwLbR .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-mHPPPpseMyagwLbR .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-mHPPPpseMyagwLbR .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-mHPPPpseMyagwLbR .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-mHPPPpseMyagwLbR .marker{fill:#333333;stroke:#333333;}#mermaid-svg-mHPPPpseMyagwLbR .marker.cross{stroke:#333333;}#mermaid-svg-mHPPPpseMyagwLbR svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-mHPPPpseMyagwLbR p{margin:0;}#mermaid-svg-mHPPPpseMyagwLbR .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mHPPPpseMyagwLbR text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-mHPPPpseMyagwLbR .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mHPPPpseMyagwLbR .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-mHPPPpseMyagwLbR .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-mHPPPpseMyagwLbR .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-mHPPPpseMyagwLbR #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-mHPPPpseMyagwLbR .sequenceNumber{fill:white;}#mermaid-svg-mHPPPpseMyagwLbR #sequencenumber{fill:#333;}#mermaid-svg-mHPPPpseMyagwLbR #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-mHPPPpseMyagwLbR .messageText{fill:#333;stroke:none;}#mermaid-svg-mHPPPpseMyagwLbR .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mHPPPpseMyagwLbR .labelText,#mermaid-svg-mHPPPpseMyagwLbR .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-mHPPPpseMyagwLbR .loopText,#mermaid-svg-mHPPPpseMyagwLbR .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-mHPPPpseMyagwLbR .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-mHPPPpseMyagwLbR .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-mHPPPpseMyagwLbR .noteText,#mermaid-svg-mHPPPpseMyagwLbR .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-mHPPPpseMyagwLbR .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mHPPPpseMyagwLbR .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mHPPPpseMyagwLbR .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-mHPPPpseMyagwLbR .actorPopupMenu{position:absolute;}#mermaid-svg-mHPPPpseMyagwLbR .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-mHPPPpseMyagwLbR .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-mHPPPpseMyagwLbR .actor-man circle,#mermaid-svg-mHPPPpseMyagwLbR line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-mHPPPpseMyagwLbR :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} alt缓存有G缓存无G go f()systemstack(newproc1)gfget(pp)复用的Gmalg(stackMin=8KB)设置栈帧+sched+goidcasgstatus(_Gdead→_Grunnable)newgrunqput(pp, newg, next=true)wakep() (如有空闲P且无自旋M)

3.3 schedule → findRunnable → execute (proc.go:4135)

go 复制代码
func schedule() {
    mp := getg().m
top:
    pp := mp.p.ptr()
    pp.preempt = false

    // ★ 核心:找到可运行的 G(阻塞直到有工作)
    gp, inheritTime, tryWakeP := findRunnable()

    if mp.spinning { resetspinning() }
    if tryWakeP { wakep() }
    if gp.lockedm != 0 { startlockedm(gp); goto top }

    execute(gp, inheritTime)  // 切换到 G 执行
}

3.4 findRunnable --- 寻找可运行G的12级优先级 (proc.go:3389)

优先级 来源 描述
0 GC STW - gcwaiting → gcstopm
1 SafePointFn - 运行安全点函数
2 Trace Reader - 追踪器读取G
3 GC Worker - GC标记工作者
4 全局队列(61次) sched.lock 保证公平性
5 Finalizer - 唤醒finalizer G
6 本地runq 无锁 最常用来源
7 全局队列 sched.lock 批量获取
8 Netpoll - 非阻塞网络轮询
9 Work Stealing - 从其他P偷G
10 空闲GC Worker - 空闲做标记
11 阻塞Netpoll - 等待网络事件
12 Park M - 无工作→park

#mermaid-svg-XjRqiA6QyE7JXrIx{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XjRqiA6QyE7JXrIx .error-icon{fill:#552222;}#mermaid-svg-XjRqiA6QyE7JXrIx .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XjRqiA6QyE7JXrIx .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XjRqiA6QyE7JXrIx .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XjRqiA6QyE7JXrIx .marker.cross{stroke:#333333;}#mermaid-svg-XjRqiA6QyE7JXrIx svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XjRqiA6QyE7JXrIx p{margin:0;}#mermaid-svg-XjRqiA6QyE7JXrIx .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx .cluster-label text{fill:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx .cluster-label span{color:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx .cluster-label span p{background-color:transparent;}#mermaid-svg-XjRqiA6QyE7JXrIx .label text,#mermaid-svg-XjRqiA6QyE7JXrIx span{fill:#333;color:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx .node rect,#mermaid-svg-XjRqiA6QyE7JXrIx .node circle,#mermaid-svg-XjRqiA6QyE7JXrIx .node ellipse,#mermaid-svg-XjRqiA6QyE7JXrIx .node polygon,#mermaid-svg-XjRqiA6QyE7JXrIx .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XjRqiA6QyE7JXrIx .rough-node .label text,#mermaid-svg-XjRqiA6QyE7JXrIx .node .label text,#mermaid-svg-XjRqiA6QyE7JXrIx .image-shape .label,#mermaid-svg-XjRqiA6QyE7JXrIx .icon-shape .label{text-anchor:middle;}#mermaid-svg-XjRqiA6QyE7JXrIx .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XjRqiA6QyE7JXrIx .rough-node .label,#mermaid-svg-XjRqiA6QyE7JXrIx .node .label,#mermaid-svg-XjRqiA6QyE7JXrIx .image-shape .label,#mermaid-svg-XjRqiA6QyE7JXrIx .icon-shape .label{text-align:center;}#mermaid-svg-XjRqiA6QyE7JXrIx .node.clickable{cursor:pointer;}#mermaid-svg-XjRqiA6QyE7JXrIx .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XjRqiA6QyE7JXrIx .arrowheadPath{fill:#333333;}#mermaid-svg-XjRqiA6QyE7JXrIx .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XjRqiA6QyE7JXrIx .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XjRqiA6QyE7JXrIx .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XjRqiA6QyE7JXrIx .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XjRqiA6QyE7JXrIx .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XjRqiA6QyE7JXrIx .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XjRqiA6QyE7JXrIx .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XjRqiA6QyE7JXrIx .cluster text{fill:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx .cluster span{color:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XjRqiA6QyE7JXrIx .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XjRqiA6QyE7JXrIx rect.text{fill:none;stroke-width:0;}#mermaid-svg-XjRqiA6QyE7JXrIx .icon-shape,#mermaid-svg-XjRqiA6QyE7JXrIx .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XjRqiA6QyE7JXrIx .icon-shape p,#mermaid-svg-XjRqiA6QyE7JXrIx .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XjRqiA6QyE7JXrIx .icon-shape .label rect,#mermaid-svg-XjRqiA6QyE7JXrIx .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XjRqiA6QyE7JXrIx .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XjRqiA6QyE7JXrIx .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XjRqiA6QyE7JXrIx :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
有G

有G

有事件

偷到
没偷到


findRunnable()
gcwaiting?
Trace Reader?
GC Worker?
schedtick%61==0

&& 全局队列非空?
Finalizer?
本地runq

(runqget)
全局队列

(globrunqgetbatch)
Netpoll(0)
Work Stealing

(stealWork)
空闲GC Mark Worker
阻塞Netpoll

(netpoll(blocking))
Park M

(stopm)
gcstopm() → goto top
return G, inheritTime
return G
return netpoll G
return stolen G
return idle GC worker

3.5 runqput --- 放入本地队列 (proc.go:7478)

go 复制代码
func runqput(pp *p, gp *g, next bool) {
    if next {
        // 放入 runnext 槽位(优先调度)
        // 原来的 runnext 被推到 runq 中
        oldnext := pp.runnext
        if !pp.runnext.CompareAndSwap(oldnext, guintptr(unsafe.Pointer(gp))) {
            goto slow
        }
        if oldnext == 0 { return }
        gp = oldnext.ptr()  // gp 现在是旧的 runnext
    }
slow:
    // 放入 runq 环形队列
    h := pp.runqhead
    t := pp.runqtail
    if t-h < uint32(len(pp.runq)) {
        pp.runq[t%uint32(len(pp.runq))].set(gp)
        pp.runtail.Store(t + 1)  // 原子更新 tail
        return
    }
    // 本地队列满 → 转移一半到全局队列
    runqputslow(pp, gp, h, t)
}

设计要点

  • runnext 是一个原子变量,放的是"由当前G ready的、应优先运行的G"
  • 本地队列是环形缓冲区,最多256个G
  • 队列满时,转移一半到全局队列(避免单P独占)

3.6 runqget --- 从本地队列获取 (proc.go:7598)

go 复制代码
func runqget(pp *p) (gp *g, inheritTime bool) {
    // 1. 先检查 runnext
    next := pp.runnext
    if next != 0 {
        pp.runnext = 0
        gp = next.ptr()
        // runnext 的 G 不继承时间片 → 可能被抢占
        return gp, false
    }
    // 2. 从 runq 环形队列取
    for {
        h := pp.runqhead
        t := pp.runqtail.Load()
        if t == h { return nil, false }
        gp = pp.runq[h%uint32(len(pp.runq))].ptr()
        if pp.runqhead.CompareAndSwap(h, h+1) {
            return gp, true  // 继承时间片
        }
    }
}

3.7 stealWork --- Work Stealing (proc.go:7662)

go 复制代码
func runqgrab(pp *p, batch *[256]guintptr, batchHead uint32, stealRunNextG bool) uint32 {
    // 从 p2 的 runq 中偷取一半的 G
    // batch 放偷到的 G
    // stealRunNextG: 是否偷 runnext (通常只在最后尝试)

    for {
        h := pp2.runqhead
        t := pp2.runqtail.Load()
        n := t - h
        n = n - n/2  // 偷一半
        if n == 0 { ... }
        // CAS 更新 head
        // 复制 G 到 batch
    }
}

Stealing 策略

  1. 随机选择一个 P
  2. 从其 runq 偷取一半的 G
  3. 如果 runq 为空,尝试偷 runnext(需要竞争)
  4. 最多尝试 4 次

四、sysmon --- 系统监控线程 (proc.go:6486)

sysmon 是一个不绑定 P 的独立 M,负责:

go 复制代码
func sysmon() {
    idle := 0
    delay := uint32(0)
    for {
        // 动态休眠: 20us → 10ms
        if idle == 0 { delay = 20 }
        else if idle > 50 { delay *= 2 }
        if delay > 10*1000 { delay = 10*1000 }
        usleep(delay)

        // 1. 轮询网络 (每10ms)
        if lastpoll + 10*1000*1000 < now {
            netpoll(0)  // 非阻塞
        }

        // 2. Retake P (从长时间系统调用中夺回P)
        if retake(now) != 0 { idle = 0 }

        // 3. 强制 GC (2分钟未GC)
        if gcTrigger{kind: gcTriggerTime}.test() {
            gcStart(...)
        }

        // 4. 抢占长时间运行的 G
        // preemptone(pp)

        // 5. 深度休眠 (所有P空闲时)
        if sched.npidle.Load() == gomaxprocs {
            notetsleep(&sched.sysmonnote, sleep)
        }
    }
}

retake --- P 回收 (proc.go:6630)

场景 P 状态 操作
系统调用超过20μs _Psyscall handoffp → 给其他 M
G 运行超过10ms _Prunning preemptone → 发送抢占信号

#mermaid-svg-hpifPzPDhrXYEifh{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-hpifPzPDhrXYEifh .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-hpifPzPDhrXYEifh .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-hpifPzPDhrXYEifh .error-icon{fill:#552222;}#mermaid-svg-hpifPzPDhrXYEifh .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-hpifPzPDhrXYEifh .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-hpifPzPDhrXYEifh .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-hpifPzPDhrXYEifh .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-hpifPzPDhrXYEifh .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-hpifPzPDhrXYEifh .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-hpifPzPDhrXYEifh .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-hpifPzPDhrXYEifh .marker{fill:#333333;stroke:#333333;}#mermaid-svg-hpifPzPDhrXYEifh .marker.cross{stroke:#333333;}#mermaid-svg-hpifPzPDhrXYEifh svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-hpifPzPDhrXYEifh p{margin:0;}#mermaid-svg-hpifPzPDhrXYEifh .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-hpifPzPDhrXYEifh .cluster-label text{fill:#333;}#mermaid-svg-hpifPzPDhrXYEifh .cluster-label span{color:#333;}#mermaid-svg-hpifPzPDhrXYEifh .cluster-label span p{background-color:transparent;}#mermaid-svg-hpifPzPDhrXYEifh .label text,#mermaid-svg-hpifPzPDhrXYEifh span{fill:#333;color:#333;}#mermaid-svg-hpifPzPDhrXYEifh .node rect,#mermaid-svg-hpifPzPDhrXYEifh .node circle,#mermaid-svg-hpifPzPDhrXYEifh .node ellipse,#mermaid-svg-hpifPzPDhrXYEifh .node polygon,#mermaid-svg-hpifPzPDhrXYEifh .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-hpifPzPDhrXYEifh .rough-node .label text,#mermaid-svg-hpifPzPDhrXYEifh .node .label text,#mermaid-svg-hpifPzPDhrXYEifh .image-shape .label,#mermaid-svg-hpifPzPDhrXYEifh .icon-shape .label{text-anchor:middle;}#mermaid-svg-hpifPzPDhrXYEifh .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-hpifPzPDhrXYEifh .rough-node .label,#mermaid-svg-hpifPzPDhrXYEifh .node .label,#mermaid-svg-hpifPzPDhrXYEifh .image-shape .label,#mermaid-svg-hpifPzPDhrXYEifh .icon-shape .label{text-align:center;}#mermaid-svg-hpifPzPDhrXYEifh .node.clickable{cursor:pointer;}#mermaid-svg-hpifPzPDhrXYEifh .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-hpifPzPDhrXYEifh .arrowheadPath{fill:#333333;}#mermaid-svg-hpifPzPDhrXYEifh .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-hpifPzPDhrXYEifh .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-hpifPzPDhrXYEifh .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hpifPzPDhrXYEifh .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-hpifPzPDhrXYEifh .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hpifPzPDhrXYEifh .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-hpifPzPDhrXYEifh .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-hpifPzPDhrXYEifh .cluster text{fill:#333;}#mermaid-svg-hpifPzPDhrXYEifh .cluster span{color:#333;}#mermaid-svg-hpifPzPDhrXYEifh div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-hpifPzPDhrXYEifh .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-hpifPzPDhrXYEifh rect.text{fill:none;stroke-width:0;}#mermaid-svg-hpifPzPDhrXYEifh .icon-shape,#mermaid-svg-hpifPzPDhrXYEifh .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-hpifPzPDhrXYEifh .icon-shape p,#mermaid-svg-hpifPzPDhrXYEifh .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-hpifPzPDhrXYEifh .icon-shape .label rect,#mermaid-svg-hpifPzPDhrXYEifh .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-hpifPzPDhrXYEifh .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-hpifPzPDhrXYEifh .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-hpifPzPDhrXYEifh :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} Yes
No
Yes
sysmon 循环
Netpoll (每10ms)
retake(now)
强制GC (2min)
深度休眠 (全部P idle)
P 在 _Psyscall

超过 20μs?
handoffp()

P 给其他M
G 运行

超过 10ms?
preemptone()

发送 SIGURG


五、系统调用处理

5.1 entersyscall --- 进入系统调用 (proc.go:4737)

go 复制代码
func entersyscall() {
    gp := getg()
    mp := gp.m

    // 1. 保存 PC/SP/BP (GC 需要扫描栈)
    gp.syscallsp = gp.sched.sp
    gp.syscallpc = gp.sched.pc
    gp.syscallbp = gp.sched.bp

    // 2. 设置 G 状态为 _Gsyscall
    casgstatus(gp, _Grunning, _Gsyscall)

    // 3. 记录 P 的 syscalltick
    pp := mp.p.ptr()
    pp.syscalltick++

    // 4. 解绑 P (P 状态设为 _Psyscall)
    pp.m = 0
    mp.p = 0
    pp.status = _Psyscall

    // 5. 通知 sysmon (可能 retake 这个 P)
    entersyscallWakeSysmon()
}

5.2 exitsyscall --- 退出系统调用 (proc.go:4883)

go 复制代码
func exitsyscall() {
    gp := getg()

    // 快速路径: 尝试重新获取进入前的 P (oldp)
    oldp := gp.m.oldp.ptr()
    if oldp != nil && oldp.status == _Psyscall && exitsyscallFast(oldp) {
        // ★ 成功: 原来的 P 还在 → 直接恢复执行
        casgstatus(gp, _Gsyscall, _Grunning)
        return
    }

    // 慢速路径: oldp 被偷走了
    // 1. 尝试获取任意空闲 P
    // 2. 没有空闲 P → 将 G 放入全局队列,park M
    mcall(exitsyscall0)
}

sysmon Processor OS Thread Goroutine sysmon Processor OS Thread Goroutine #mermaid-svg-2s5aAIaIJ98nUftj{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-2s5aAIaIJ98nUftj .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-2s5aAIaIJ98nUftj .error-icon{fill:#552222;}#mermaid-svg-2s5aAIaIJ98nUftj .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-2s5aAIaIJ98nUftj .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-2s5aAIaIJ98nUftj .marker{fill:#333333;stroke:#333333;}#mermaid-svg-2s5aAIaIJ98nUftj .marker.cross{stroke:#333333;}#mermaid-svg-2s5aAIaIJ98nUftj svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-2s5aAIaIJ98nUftj p{margin:0;}#mermaid-svg-2s5aAIaIJ98nUftj .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2s5aAIaIJ98nUftj text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-2s5aAIaIJ98nUftj .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2s5aAIaIJ98nUftj .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-2s5aAIaIJ98nUftj .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-2s5aAIaIJ98nUftj .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-2s5aAIaIJ98nUftj #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-2s5aAIaIJ98nUftj .sequenceNumber{fill:white;}#mermaid-svg-2s5aAIaIJ98nUftj #sequencenumber{fill:#333;}#mermaid-svg-2s5aAIaIJ98nUftj #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-2s5aAIaIJ98nUftj .messageText{fill:#333;stroke:none;}#mermaid-svg-2s5aAIaIJ98nUftj .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2s5aAIaIJ98nUftj .labelText,#mermaid-svg-2s5aAIaIJ98nUftj .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-2s5aAIaIJ98nUftj .loopText,#mermaid-svg-2s5aAIaIJ98nUftj .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-2s5aAIaIJ98nUftj .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-2s5aAIaIJ98nUftj .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-2s5aAIaIJ98nUftj .noteText,#mermaid-svg-2s5aAIaIJ98nUftj .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-2s5aAIaIJ98nUftj .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2s5aAIaIJ98nUftj .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2s5aAIaIJ98nUftj .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-2s5aAIaIJ98nUftj .actorPopupMenu{position:absolute;}#mermaid-svg-2s5aAIaIJ98nUftj .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-2s5aAIaIJ98nUftj .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-2s5aAIaIJ98nUftj .actor-man circle,#mermaid-svg-2s5aAIaIJ98nUftj line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-2s5aAIaIJ98nUftj :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 进入系统调用 退出系统调用 (快速路径) 成功! 直接恢复执行 退出系统调用 (慢速路径) alt有空闲P无空闲P entersyscall()保存 PC/SP/BPcasgstatus(_Grunning → _Gsyscall)解绑 P, status=_PsyscallentersyscallWakeSysmon()exitsyscall()CAS oldp.status _Psyscall→_PrunningCAS oldp 失败 (已被偷)pidleget() 获取空闲Pacquirep()globrunqput(gp)stopm() park


六、M/P 生命周期管理

6.1 wakep --- 唤醒/创建 M (proc.go:3212)

go 复制代码
func wakep() {
    // 条件: 有空闲P && 无自旋M
    if sched.nmspinning.Load() != 0 || sched.npidle.Load() == 0 {
        return
    }
    if !sched.nmspinning.CompareAndSwap(0, 1) { return }

    pp := pidleget(0)  // 获取空闲P
    if pp == nil {
        sched.nmspinning.Add(-1)
        return
    }
    startm(pp, true, false)  // spinning=true → 新M立即开始自旋
}

6.2 startm --- 启动 M (proc.go:3035)

go 复制代码
func startm(pp *p, spinning, lockheld bool) {
    // 1. 尝试获取空闲 M
    mp := mget()

    if mp == nil {
        // 2. 无空闲M → 创建新M
        newm(fn, pp, id)
        return
    }

    // 3. 预装 P
    mp.nextp.set(pp)

    // 4. 唤醒 M
    notewakeup(&mp.park)
}

6.3 M 启动流程: mstart → mstart1 → schedule

#mermaid-svg-a62t5s5qIeweRZha{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-a62t5s5qIeweRZha .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-a62t5s5qIeweRZha .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-a62t5s5qIeweRZha .error-icon{fill:#552222;}#mermaid-svg-a62t5s5qIeweRZha .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-a62t5s5qIeweRZha .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-a62t5s5qIeweRZha .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-a62t5s5qIeweRZha .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-a62t5s5qIeweRZha .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-a62t5s5qIeweRZha .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-a62t5s5qIeweRZha .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-a62t5s5qIeweRZha .marker{fill:#333333;stroke:#333333;}#mermaid-svg-a62t5s5qIeweRZha .marker.cross{stroke:#333333;}#mermaid-svg-a62t5s5qIeweRZha svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-a62t5s5qIeweRZha p{margin:0;}#mermaid-svg-a62t5s5qIeweRZha .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-a62t5s5qIeweRZha .cluster-label text{fill:#333;}#mermaid-svg-a62t5s5qIeweRZha .cluster-label span{color:#333;}#mermaid-svg-a62t5s5qIeweRZha .cluster-label span p{background-color:transparent;}#mermaid-svg-a62t5s5qIeweRZha .label text,#mermaid-svg-a62t5s5qIeweRZha span{fill:#333;color:#333;}#mermaid-svg-a62t5s5qIeweRZha .node rect,#mermaid-svg-a62t5s5qIeweRZha .node circle,#mermaid-svg-a62t5s5qIeweRZha .node ellipse,#mermaid-svg-a62t5s5qIeweRZha .node polygon,#mermaid-svg-a62t5s5qIeweRZha .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-a62t5s5qIeweRZha .rough-node .label text,#mermaid-svg-a62t5s5qIeweRZha .node .label text,#mermaid-svg-a62t5s5qIeweRZha .image-shape .label,#mermaid-svg-a62t5s5qIeweRZha .icon-shape .label{text-anchor:middle;}#mermaid-svg-a62t5s5qIeweRZha .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-a62t5s5qIeweRZha .rough-node .label,#mermaid-svg-a62t5s5qIeweRZha .node .label,#mermaid-svg-a62t5s5qIeweRZha .image-shape .label,#mermaid-svg-a62t5s5qIeweRZha .icon-shape .label{text-align:center;}#mermaid-svg-a62t5s5qIeweRZha .node.clickable{cursor:pointer;}#mermaid-svg-a62t5s5qIeweRZha .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-a62t5s5qIeweRZha .arrowheadPath{fill:#333333;}#mermaid-svg-a62t5s5qIeweRZha .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-a62t5s5qIeweRZha .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-a62t5s5qIeweRZha .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a62t5s5qIeweRZha .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-a62t5s5qIeweRZha .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a62t5s5qIeweRZha .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-a62t5s5qIeweRZha .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-a62t5s5qIeweRZha .cluster text{fill:#333;}#mermaid-svg-a62t5s5qIeweRZha .cluster span{color:#333;}#mermaid-svg-a62t5s5qIeweRZha div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-a62t5s5qIeweRZha .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-a62t5s5qIeweRZha rect.text{fill:none;stroke-width:0;}#mermaid-svg-a62t5s5qIeweRZha .icon-shape,#mermaid-svg-a62t5s5qIeweRZha .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-a62t5s5qIeweRZha .icon-shape p,#mermaid-svg-a62t5s5qIeweRZha .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-a62t5s5qIeweRZha .icon-shape .label rect,#mermaid-svg-a62t5s5qIeweRZha .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-a62t5s5qIeweRZha .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-a62t5s5qIeweRZha .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-a62t5s5qIeweRZha :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} mstart()
mstart0()
mstart1()
mstartm0()

(仅m0)
schedule()
findRunnable()
execute(gp)
gogo(&gp.sched)

(汇编:切换栈)
用户代码执行
goexit()
schedule()


七、自旋M管理 --- "Delicate Dance"

7.1 为什么需要自旋M?

自旋M = 正在寻找工作但还没park的M。自旋的目的是减少延迟

  • 当新G出现时,自旋M可以立即执行,无需唤醒parked M
  • 但自旋太多浪费CPU

7.2 自旋规则

条件 行为
提交新工作 && 有空闲P && 无自旋M wakep() 创建新自旋M
自旋M找到工作 停止自旋,执行G;如果还有工作可能再wakep
自旋M没找到工作 停止自旋,park
2×nmspinning < GOMAXPROCS-npidle 允许新M进入自旋
nmspinning ≥ GOMAXPROCS/2 不允许更多自旋M

八、runq 数据结构详解

8.1 本地 runq --- 环形队列

复制代码
P.runq[256]  (环形缓冲区)
  ┌─────┬─────┬─────┬─────┬─────┬─────┐
  │ G1  │ G2  │ G3  │ ... │     │     │
  └─────┴─────┴─────┴─────┴─────┴─────┘
    ^head                        ^tail
  runqhead=0                  runqtail=3

  runnext = G0 (优先调度)

为什么 runnext 单独存储?

  • runnext 是当前G ready的G,局部性好
  • 放入 runnext 比放入 runq 更快(一次 CAS)
  • 下次调度优先执行 runnext,保持依赖G的局部性

8.2 全局 runq --- 链表

go 复制代码
type gQueue struct {
    head, tail guintptr  // 双向链表
}

全局队列在以下场景使用:

  • 本地队列满 → runqputslow 转移一半到全局
  • 某些特殊G (finalizer/cgo回调) 直接放入全局
  • schedtick%61==0 时检查全局,防止饥饿

九、设计模式总结

# 模式 体现
1 GMP 模型 G(协程) + M(线程) + P(处理器) 三级调度
2 Work Stealing 空闲P从忙碌P偷G,负载均衡
3 Handoff 系统调用时P转给其他M,不浪费
4 自旋M 减少唤醒延迟,限制自旋数量
5 本地队列无锁 per-P runq,只有拥有者操作
6 全局队列公平 每61次检查,防止饥饿
7 runnext 优先 保持G局部性
8 sysmon 独立 不绑P的监控线程
9 CAS 无锁 runq操作用CAS,全局锁最少化
10 goid 批量缓存 per-P 缓存 goid,减少原子操作
11 G 复用 gFree 池复用死G
12 systemstack 调度操作在g0栈上,G栈可移动
13 快速/慢速路径 exitsyscall 先尝试快速恢复oldp
14 动态休眠 sysmon 20us→10ms 自适应
15 抢占三机制 stackguard0/SIGURG/preempt标志
相关推荐
Scott9999HH1 小时前
【IIoT流量实战】蒸汽管道阀门全关却仍有流量?用 Python 实现涡街信号 FFT 频谱分析与温压全补偿积算网关,深度拆解靠谱的涡街流量计厂家硬核技术标准
开发语言·python
码智社2 小时前
AES加密原理详解及Java实现加解密实战
java·开发语言
AI云海2 小时前
python 列表、元组、集合和字典
开发语言·python
萧瑟余晖3 小时前
JDK 26 新特性详解
java·开发语言
马优晨4 小时前
Freemarker 完整讲解(后端 Java 模板引擎)
java·开发语言·freemarker·freemarker 完整讲解·freemarker模板引擎
人邮异步社区6 小时前
怎么把C语言学到精通?
c语言·开发语言
心平气和量大福大6 小时前
C#-WPF-控件-TextBox 数据绑定
开发语言·c#·wpf
ttwuai7 小时前
Cursor 生成 CRUD 后,Go 后台接口别只测 200:JWT、RBAC 和 tenant_id 怎么验
开发语言·后端·golang
এ慕ོ冬℘゜7 小时前
前端基础:什么是时间戳?JS获取时间戳三种方法与实战用途
开发语言·前端·javascript
执明wa7 小时前
LayoutInflater详解: XML是如何变成View的?
android·xml·开发语言·android studio