4.2 KiB
4.2 KiB
tags, create time
| tags | create time | |||||
|---|---|---|---|---|---|---|
|
2026-06-07 16:00 |
GMP 调度原理 — 数据结构
概述
本节深入 runtime/runtime2.go,逐字段拆解 G、M、P、schedt 四个核心结构体的源码定义及其设计意图。理解这些结构是后续追踪调度流程的基础。
正文
2.1 G(Goroutine)
G 是 goroutine 的运行时实体,承载栈空间、执行状态和生命周期信息。
type g struct {
stack stack // 栈空间 [stack.lo, stack.hi)
stackguard0 uintptr // 栈保护区边界;也用于传递抢占标记
_panic *_panic // panic 链表头
_defer *_defer // defer 链表头(LIFO)
m *m // 当前绑定的 M(未运行时为 nil)
atomicstatus uint32 // 原子状态:_Gidle → _Grunnable → _Grunning → ...
schedlink guintptr // 全局队列 / 空闲链表中的 next 指针
}
| 关键字段 | 说明 |
|---|---|
stack |
Goroutine 的执行栈,初始约 2KB,可动态扩容 |
stackguard0 |
函数调用前比较值。若等于 stackPreempt 表示被标记抢占;若接近 stack.lo 则触发栈扩容 |
atomicstatus |
生命周期状态的原子快照,通过 casgstatus() 切换 |
状态流转:
stateDiagram-v2
[*] --> _Gidle: 未初始化
_Gidle --> _Grunnable: newproc()
_Grunnable --> _Grunning: schedule() 从队列取出
_Grunning --> _Grunnable: Gosched() / preempt()
_Grunning --> _Gdead: 执行完毕
_Grunning --> _Gwaiting: gopark() 阻塞
_Gwaiting --> _Grunnable: goready() 唤醒
_Gwaiting --> _Gsyscall: (间接)
_Gsyscall --> _Grunning: exitsyscall()
_Gdead --> [*]: 回收
style _Grunning fill:#e8f5e9
style _Gwaiting fill:#fff3e0
2.2 M(Machine)
M 是 OS 线程的运行时封装,真正执行代码。
type m struct {
g0 *g // 调度协程,每个 M 独有
procid uint64 // M 的唯一 ID
gsignal *g // 信号处理协程
curg *g // 当前正在运行的用户 G
p puintptr // 当前绑定的 P
schedlink muintptr // 空闲 M 链表 next 指针
}
M 在两个角色间切换:
- 执行
g0时:调度者——调用schedule()寻找下一个待执行的 G - 执行
curg时:执行者——运行用户代码
2.3 P(Processor)
P 是逻辑处理器,作为调度器的核心组件管理本地队列。
type p struct {
id int32 // P 的编号
status uint32 // _Pidle / _Prunning / _Psyscall
link puintptr // 空闲 P 链表
schedtick uint32 // 每次 schedule() 自增
syscalltick uint32 // 每次系统调用自增
m muintptr // 回指绑定的 M(idle 时为 0)
runqhead uint32 // LRQ 头部索引
runqtail uint32 // LRQ 尾部索引
runq [256]guintptr // 本地 G 队列(环形数组)
runnext guintptr // VIP 位置:高优先级下一个 G
}
| 关键字段 | 作用 |
|---|---|
runq[256] |
定长环形数组作为 LRQ,CAS 无锁存取 |
runnext |
新创建的 G 优先放入此处,下次调度直接执行,跳过队列开销 |
schedtick |
配合防饥饿机制:schedtick % 61 == 0 时检查 GRQ |
2.4 schedt(全局调度器)
schedt 管理跨 P 的全局资源,访问需持有 sched.lock。
type schedt struct {
lock mutex // 全局互斥锁
midle muintptr // 空闲 M 队列
pidle puintptr // 空闲 P 队列
runq gQueue // 全局 G 队列(GRQ)
runqsize int32 // GRQ 中 G 的数量
// ...
}
[!note] 📝 idle 队列的设计意图
midle和pidle实现了资源的休眠与复用——不忙时释放回池中,有新任务时快速唤醒,避免 CPU 空转或频繁创建线程。