G
1 .goroutine的新建,休眠,恢复,停止都会受到go运行时的管理
2 .goroutine执行异步操作时,等操作完成之后在恢复,不会占用系统线程
3 .goroutine新建或者恢复会添加到运行队列,等待M取出并运行
4 .G的状态
1 .空闲中:idle:G刚创建,没有初始化
2 .待运行:runnable:g正在运行队列中,等待M取出并运行
3 .运行中:running:M正在运行这个G,这时M会拥有一个P
4 .系统调用:syscall:表示这个M这个在运行这个g发起的系统调用,这时M并不拥有p
5 .等待中waiting:表示g在等待某些条件完成。此时g不在运行也不再运行队列中,可能在channel等待队列中
6 .已终止:dead,表示G未被使用,可能已经执行完毕
7 .栈复制中:copystack,表示g正在获取一个新的栈空间并把原来的内容复制过去,用于防止GC扫描
5.G里面重要的成员
1.stack:当前g使用的栈空间
2.stackguard0:检查当前栈空间是否有足够的值,低于这个值就扩张栈,0是go代码使用的
3.stackguard1:检查栈空间是否有足够的值,如果低于这个值就会扩张栈,1是原生代码使用的
4.m:对应g当前的m
5.sched:g的调度数据,当g中断时会保存当前的pc,rsp等值到这里,恢复运行时使用这里的值
6.atomicstatus:g当前的状态
7.schedlink:下一个g,当g在链表结构中会使用
8.preempt:g是否被抢占中
9.lockedm:g是否要求要回到这个M执行,有时g中断之后会要求使用原来的m来执行
M
1 .在当前golang中等于系统线程
2 .m运行以下代码:go代码,即goroutine,M运行go代码需要一个p提供环境。原生代码,例如阻塞的syscall.m运行原生代码不需要p
3 .m会从运行队列中取出G,然后运行G,如果G运行完毕或者进入休眠状态,则从队列中取出下一个g运行,周而复始。
4 .有时g需要调用一些无法避免阻塞的原生代码,这个时候m会释放持有的p并进入阻塞状态,其他m会取得这个p并继续运行队列中的G
5 .go需要保证有足够多的m来运行G,不会让cpu闲着,也会保证m的数量不能太多
6 .m的状态
1 .自旋中:m正在从运行队列获取g,这个时候m会拥有一个p,是否唤醒或者创建新的m取决于当前自旋中的m的数量
2.执行go代码:m正在执行go代码,这时m会有一个p
3.执行原生代码:m正在执行原生代码或者阻塞的syscall,这时m没有p
4.休眠中:m发现无待运行的g会进入休眠,并添加到空闲m链表中,这时m没有p
5.
7.空闲m链表
1.进入休眠的M会等待一个信号量m.park,唤醒休眠的m会使用这个信号量
2.go需要保证有足够的m可以运行g
3.入队待运行的G之后,如果当前无自旋的M但是有空闲的P,就唤醒或者新建一个M
4.M离开自旋状态并运行出队的G时(出队是指从本地或者全局队列中出队在m上运行),如果当前无自选的但是有空闲的p,就唤醒或者新建一个m
5.当m离开自旋状态准备休眠时,会在离开后再次检查所有运行队列,如果有待运行的g重新进入自旋状态
6.
8.M里面重要的成员
1 .g0用于调度特殊的g,调度和执行系统时会切换到这个g
2.curg:当前运行的g
3.p:当前拥有的p
4.nextp:唤醒m时,m会有这个p
5.park:M休眠时使用的信号量,唤醒M时会通过他唤醒
6.schedllink:下一个m,当m在链表结构中会使用
7.mache:分配内存时使用的本地分配器,和p.mache一样
8.lockedg:lockedm的对应值
p
1.代表M运行G需要的资源
2.p的数量默认为是cpu核数,但是可以动态设置
3.p就是控制go代码并行度的机制。
4.如果p的数量等于1,那就证明当前最多有一个线程来执行go代码
5.执行原生代码的线程数量是不受p的控制
6.因为同一个时间只有一个m可以有p,此时p的数据都是锁自由的,所以读写这些数据非常效率高
7.p的状态
1.空闲中idle:当m发现无待运行的G时会进入休眠,这时M拥有的p会变为空闲并加入到空闲p链表中
2.运行中running:当拥有一个p之后,这个p的状态就会变为运行中,M执行G会使用这个p中的资源
3.系统调用中:syscall当go调用原生代码的时候,原生代码反过来又调用go代码时,使用的p会变成此状态
4.GC停止中:当gc停止了整个世界时,p会变为这个状态
5.dead已停止:当p的数量在运行时改变,且数量减少时多余的p会变为这个状态
8.空闲p链表
1.当本地的运行队列中的所有G都运行完毕,又不能从其他地方拿到G时
2.拥有p的M会释放p并进入休眠状态,释放的p会变为空闲状态并加入空闲p链表中,空闲p链表保存在全局变量sched中
3.下次待运行的G入队时如果发现有空闲的p,但是有没有自旋中的M会唤醒或者新建一个m,m会拥有这个p.p会重新变成运行中
9.p里面重要的值
1.status:p当前的状态
2.link:下一个p,当p在链表结构中会使用
3.m:拥有这个p的m
4.mache:分配内存时使用的本地分配器
5.runqhead:本地运行队列的出队序号
6.runqtail:本地运行队列的入队序号
7.gfree:G的自由列表,保存为_Gdead后可以复用g的实例
8.gcBgMarkWorker:后台CG的worker函数,如果它存在M会优先执行他
9.gcw:GC的本地功作队列
本地运行队列
1.在go中有多个运行队列可以保存运行的G,分别是p的本地队列和全局队列
2.入队待运行的G,会优先添加到当前p的本地队列,m获取待运行的G时也会优先从拥有的p的本地队列获取
3.本地运行队列有数量上限,超过256个会入队到全局队列
4.本地队列入队和出队不需要使用线程锁
5.本地队列是一个环形队列,长256的数组和两个序号组成
6.当m从p的本地运行队列获取G时,如果发现本地队列为空,会从其他的p盗取一半的G过来
全局队列
1.全局队列保存在全局变量sched中,全局运行队列入队和出队需要使用线程锁
M0和G0
1.go中有特殊的M和G,分别是M0和G0
2.m0是骑宠程序后的主线程,这个m对应的实例会在全局变量m0中,不需要再heap上分配
3.m0负责执行初始化操作和启动第一个g,之后m0就和其他的m一样了
4.g0仅是负责调度的g,g0不指向任何可执行的函数,每个m都会有一个自己的g0
5.在调度或者系统调用的时候会使用g0的栈空间,全局变量的g0是m0的g0