【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标志
相关推荐
阿坤带你走近大数据2 小时前
java中泛型不能用基础数据类型
java·开发语言
weixin_307779132 小时前
从脚本执行到智能体协作:AI辅助测试能力的范式重构
运维·开发语言·人工智能·算法·测试用例
云絮.2 小时前
增删改查操作
java·开发语言
themingyi2 小时前
Abaqus2024安装python包pandas
开发语言·python·pandas
阿正的梦工坊2 小时前
【Rust】19-FFI、ABI 与跨语言边界设计
开发语言·后端·rust
殇淋狱陌2 小时前
Python列表知识思维导图
开发语言·python·学习
代码中介商2 小时前
C++ 智能指针完全指南(三):weak_ptr 与循环引用
开发语言·c++
fox_lht2 小时前
第十五章 函数式语言:迭代器和闭包
开发语言·后端·学习·算法·rust
Web极客码2 小时前
如何通过 Python + LLM 用最少的 Token 完成精准推荐任务
开发语言·人工智能·python·ai