Go面试核心考点之GMP调度器源码级深度解析
文章导语
GMP调度器是Go面试中最高频的考点之一。面试官不会满足于"G是goroutine,M是线程,P是处理器"这样的浅层回答。本文从源码级别剖析GMP调度的完整机制,助你面试不虚。
一、GMP核心结构
go
// G: goroutine
type g struct {
stack stack // 栈内存
m *m // 当前绑定的M
sched gobuf // 调度信息(sp/pc)
atomicstatus uint32 // G状态
}
// M: machine (OS线程)
type m struct {
g0 *g // 调度goroutine
curg *g // 当前运行的G
p puintptr // 绑定的P
}
// P: processor (逻辑处理器)
type p struct {
m muintptr // 绑定的M
runqhead uint32
runqtail uint32
runq [256]guintptr // 本地运行队列
runnext guintptr // 下一个优先运行的G
}
二、调度流程
1. M与P绑定后,从P的runq获取G执行
2. runq为空→从全局队列取
3. 全局队列也为空→从其他P窃取(work stealing)
4. 仍为空→M进入自旋或休眠
三、工作窃取(Work Stealing)
go
// findrunnable的核心流程
// 1. 每61次调度检查一次全局队列(防止全局队列饿死)
// 2. 检查P.runnext
// 3. 从P.runq取
// 4. 从全局队列取
// 5. 从netpoller取
// 6. 从其他P窃取
四、面试高频问答
Q: 何时发生调度?
A: 系统调用阻塞、channel操作、timeSleep、抢占调度
Q: GOMAXPROCS的作用?
A: 决定同时运行的P的数量,即并行度
Q: 为什么要有P?
A: 解耦G和M,M是OS资源,G是用户态协程
五、全文总结
掌握GMP源码能让你在面试中降维打击。核心记忆点:本地队列→全局队列→窃取→netpoller→休眠。
参考文献
- Go源码 runtime/proc.go
- Go调度器设计文档
- 《Go语言设计与实现》调度器章节