八股文-Golang GMP模型

GMP模型

G(Goroutine)

Go协程,每个go关键字都会创建一个协程。

M(Machine)

操作系统线程,数量对应真实的CPU数()。

P(Processor)

处理器(go自定义概念,非CPU),线程和goroutine的中间层,其数量可通过GOMAXPROCS来设置,默认为核心数。

M必须拥有P才可以执行G中的代码,P含有一个包含多个G的队列,P可以调度G交由M执行。

特殊的M0G0

M0是启动程序后的编号为0的主线程,M0负责执行初始化操作和启动第一个G,其余和其他的M一致。
G0是每个M第一个创建的goroutine,仅负责调度,不指向任何可执行的函数,与M是1:1对应关系。在调度或系统调用时会使用G0的栈空间 (system stack)

调度器

go func()的调度流程

在调度器中,调度队列分为局部队列和全局队列。本地的运行队列是一个使用数组构成的环形链表,它最多可以存储256个待执行任务。当局部队列满时,会将G放入全局队列。
go 1.1在GM模型中引入了处理器P,增加中间层;并在处理器P的基础上实现基于工作窃取(work-stealing)的调度器,当前本地的运行队列中不包含goroutine时,调用stealWork从其它P的队列中随机获取一些goroutine。

go 1.2引入基于协作的抢占式调度,go 1.14引入基于信号的抢占式调度,但是目前只在垃圾回收扫描任务时触发。

基于协作的抢占式调度
编译器会在调用函数前插入morestack
运行时会在垃圾回收暂停程序、系统监控发现goruoutine运行超过10ms时发出抢占请求StackPreempt
当发生函数调用时,可能会执行编译器插入的morestack->newstack,检查goroutine的stackguard0字段是否为StackPreempt,若是的话,就会触发抢占让出当前线程;