- 6.1 基本结构
- 工作线程的暂止和复始
- 主要结构
- M 的结构
- P 的结构
- G 的结构
- 调度器 sched 的结构
- 总结
- 进一步阅读的参考文献
- 6.2 调度器初始化
- M 的初始化
- P 的初始化
- G 的初始化
- 一些细节
- 总结
- 6.3 调度循环
- 执行前的准备
- mstart 和 mstart1
- M 与 P 的绑定
- M 的暂止和复始
- 核心调度
- 偷取工作
- M 的唤醒
- M 的创生
- 系统线程的创建 (darwin)
- 系统线程的创建 (linux)
- M/G 解绑
- M 的死亡
- 总结
- 进一步阅读的参考文献
- 执行前的准备
- 6.4 线程管理
- LockOSThread
- UnlockOSThread
- lockedg/lockedm 与调度循环
- 模板线程
- 总结
- 进一步阅读的参考文献
- 6.5 信号处理机制
- 信号与软中断
- 初始化
- 获取原始信号屏蔽字
- 初始化信号栈
- 初始化信号屏蔽字
- 信号处理
- Extra M
- 总结
- 进一步阅读的参考文献
- 6.6 执行栈管理
- goroutine 栈结构
- 执行栈初始化
- G 的创生
- 执行栈的分配
- 小栈分配
- 大栈分配
- 堆上分配
- 执行栈的伸缩
- 分段标记
- 栈的扩张
- 栈的拷贝
- 栈的收缩
- 总结
- 6.7 协作与抢占
- 协作式调度
- 主动用户让权: Gosched
- 主动调度弃权: 栈扩张与抢占标记
- 被动监控弃权: 阻塞监控
- 抢占式调度
- 总结
- 进一步阅读的参考文献
- 协作式调度
- 6.8 运行时同步原语
- 运行时通知机制 note
- 结构
- 注册通知
- 发送通知
- 清除通知
- 运行时互斥量机制 mutex
- 结构
- lock
- unlock
- 运行时信号量机制 semaphore
- sudog 缓存
- 基于 goroutine 抽象的信号量
- 总结
- 进一步阅读的参考文献
- 运行时通知机制 note
- 6.9 系统监控
- 监控循环
- 总结
- 6.10 过去、现在与未来
- 演进史
- 单线程版调度器
- 多线程版调度器
- 工作窃取调度器
- 改进展望:非均匀访存感知的调度器设计
- 总结
- 进一步阅读的参考文献
- 演进史
- 6.11 用户层 APIs
Simplicity is complicated.
-- Rob Pike
Go 语言的调度器是笔者眼中整个运行时最迷人的组件了。
对于 Go 自身而言,它的设计和实现直接牵动了整个 Go 运行时的其他组件,是与用户态代码直接打交道的部分;
对于 Go 用户而言,调度器将极其复杂的运行时机制隐藏在了一个简单的关键字 go
之下。
为了保证高性能,调度器必须有效的利用计算的并行性和局部性原理;为了保证用户态的简洁,
调度器必须高效的对调度用户态不可见的网络轮训器、垃圾回收器进行调度;为了保证代码
执行的正确性,还必须严格的的实现用户态代码的内存顺序等等。
总而言之,调度器的设计直接决定了 Go 运行时源码的表现形式。
Go under the hood | CC-BY-NC-ND 4.0 & MIT © changkun