go语言进程无法启动 go语言遇到的坑( 四 )


系统调用阻塞
当M执行某一个G时候如果发生了阻塞操作,M会阻塞 , 如果当前有一些G在执行,调度器会把这个线程M从P中摘除,然后再创建一个新的操作系统的线程(如果有空闲的线程可用就复用空闲线程)来服务于这个P 。当M系统调用结束时候,这个G会尝试获取一个空闲的P执行,并放入到这个P的本地队列 。如果获取不到P,那么这个线程M变成休眠状态,加入到空闲线程中 , 然后这个G会被放入全局队列中 。
队列轮转
可见每个P维护着一个包含G的队列,不考虑G进入系统调用或IO操作的情况下,P周期性的将G调度到M中执行,执行一小段时间,将上下文保存下来,然后将G放到队列尾部 , 然后从队列中重新取出一个G进行调度 。
除了每个P维护的G队列以外,还有一个全局的队列,每个P会周期性地查看全局队列中是否有G待运行并将其调度到M中执行 , 全局队列中G的来源,主要有从系统调用中恢复的G 。之所以P会周期性地查看全局队列,也是为了防止全局队列中的G被饿死 。
除了每个P维护的G队列以外,还有一个全局的队列,每个P会周期性地查看全局队列中是否有G待运行并将其调度到M中执行,全局队列中G的来源,主要有从系统调用中恢复的G 。之所以P会周期性地查看全局队列 , 也是为了防止全局队列中的G被饿死 。
M0
M0是启动程序后的编号为0的主线程,这个M对应的实例会在全局变量rutime.m0中,不需要在heap上分配,M0负责执行初始化操作和启动第一个G,在之后M0就和其他的M一样了
G0
G0是每次启动一个M都会第一个创建的goroutine,G0仅用于负责调度G,G0不指向任何可执行的函数,每个M都会有一个自己的G0 , 在调度或系统调用时会使用G0的栈空间,全局变量的G0是M0的G0
一个G由于调度被中断,此后如何恢复?
中断的时候将寄存器里的栈信息,保存到自己的G对象里面 。当再次轮到自己执行时 , 将自己保存的栈信息复制到寄存器里面,这样就接着上次之后运行了 。
我这里只是根据自己的理解进行了简单的介绍,想要详细了解有关GMP的底层原理可以去看Go调度器 G-P-M 模型的设计者的文档或直接看源码
参考:()
()
go语言进程无法启动的介绍就聊到这里吧,感谢你花时间阅读本站内容,更多关于go语言遇到的坑、go语言进程无法启动的信息别忘了在本站进行查找喔 。

推荐阅读