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 策略:
- 随机选择一个 P
- 从其 runq 偷取一半的 G
- 如果 runq 为空,尝试偷 runnext(需要竞争)
- 最多尝试 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标志 |