👨🏫 GMP.
简单理解GMS调度模型原理.
秋招已经开启了,从现在开始就开始输出一些面经的东西,用于进行分享,如果有更好的建议希望联系小马进行改正。
🔖 一、一些基本概念.
- G :Goroutine,表示Go中的的协程,可以类比成Java中的协程.
- M:表示操作系统线程也可以成为内核线程,在Go的语言层面,可以把M想象成一个CPU的内核。其用runtime.m结构体表示.
- P:Go语言层面上的逻辑处理器,但不代表真正CPU数量。
OS线程负责去调度M,其中之间的关系为M:N,也就是说M是存在切换的。每一个M绑定了一个P,P负责调度对应的G来进行执行
💡 特殊的G和M
- M0:启动main方法的的主线程,,这个M实例存在runtime.m0的全局变量中,不需要在堆上进行分配内存.
- G0:每次启动一个M,都会创建一个g,每一个M第一个创建的G用于负责调度G,其不指向任何可执行的函数,每一个M都会有一个自己的G0。
🔖 二、调度过程。
- 全局队列:存放等待执行的G.
- P的本地队列:当创建一个新的G之后优先加入本地队列,本地队列满了之后,会将本地队列的一般G移动到全局队列中,本地队列保存G数量不能超过256个。
- P的列表在进行初始化的时候根据GOMAXPROCOS来进行设置,但是在Go的某个版本之后,其实根据CPU的核数来进行创建。
调度过程:
go func会创建一个G对象,该对象保存在本地队列或者是全局队列,P此时去唤醒一个M,P继续执行它的程序,M寻找是否有空闲的P,如果有空闲的,则将该G对象移动到P的本身,接下来执行一个调度循环,调用G对象执行任务、执行、清理线程、继续找新的G执行.
M执行过程中,随时会发生上下文切换,当发生上下文切换的时候,需要对执行线程保护,以便一下被调度执行时进行现场回复。Go调度器M的栈保存在G对象上,G对象只需要将M所需要的寄存器保存到G对象上就可以实现现场保护。
物理CPU内核负责调度OS操作系统内核线程来执行对应的逻辑代码,而OS内核线程是用来调度M的,这其中之间的对应比例是M:N,OS线程负责来调度M,而M是Go语言层面中线程抽象结果,M通过P调度G(任务)来执行相互对应的任务。
欢迎关注我的公众号:小马教你写Bug,一起面试一起写Bug.