调度系列
调度系列之goroutine
调度系列之m
调度系列之p
调度系列之sysmon
前面几篇对调度体系的G、M、P、sysmon分别进行了介绍。拆分的介绍有助于聚焦单一的角色,比较快地建立认识,同时也能更深入细节,但是不足以建立全局的认知。本篇在前面几篇的基础上,进行汇总的介绍。介绍的方式为选取runtime的几个阶段,介绍调度体系在对应阶段的状态,以及阶段之间的演进。
阶段1
在此阶段,runtime已经初始化,系统中存在4个p。系统的负载很低,目前只有3个goroutine,p0、p1、p2被M占据并运行goroutine,p3处于idle状态。
阶段2
此时系统的负载仍然很低,没有新的goroutine创建。
- p2上运行的goroutine结束后,对应的M进入schedule中,尝试从本地就绪队列、全局就绪队列、netpoll或者runnable的goroutine。
- 获取不到后M进入spinning状态,尝试从其他的P中获取runnable的goroutine(steal work)。steal work会尝试4次,每次随机选择一个P进行。
- M的spinning是一个非常短暂存在的状态。
阶段3
在阶段2中,占据p2的M处于spinning状态。但此时的系统中显然没有goroutine供其运行。处于spinning状态的M在获取不到goroutine后会释放占据的p,将其置为idle状态,然后将自己挂起,置于midle队列的头部。
阶段4
此时系统的负载上升,我们的生产应用绝大部分时间都处于该状态。
- 每个p的本地就绪队列以及全局的就绪队列中都有goroutine等待调度。p1、p2、p3处于running状态。
- p0上运行的goroutine在执行系统调用,处于syscall状态,此时M占据p进行系统调用。
- 另外还有M没有占据p进行系统调用,这可能是被sysmon检测到执行时间过久进行的解绑,也有可能是在系统调用之初主动handoffp。
- 也有一些goroutine处于阻塞队列中。
阶段5
- 此时p0由syscall转变为running状态;
- 可能是因为系统调用结束,M主动将p0状态由syscall -> running;
- 也可能是p0处于syscall状态的时间过长(超过10ms),被sysmon进行解绑。
- 处于syscall状态的g对应的M在系统系统调用结束后会重新尝试获取P,如果获取不到,则g挂载在全局就绪队列中,M挂载到midle中。
- 另外和goroutine绑定的M在goroutine被调度时也会挂载到midle中。
以上为调度系统的整体的介绍。