golang线程模型的3个核心元
## M
matchine的缩写,一个M代表一个内核线程
## P
processor的缩写,一个P代表执行一个go代码片段所必须的资源(工作环境)
## G
goroutine的缩写,一个G是go的代码片段
M
代表一个内核线程。大多数的情况下,创建一个M
,是因为没有足够的M来关联P
并运行其中的G
。在运行时系统执行系统监控或者垃圾回收的时候也会创建M
。
// src/runtime/runtime2.go
type m struct {
g0 *g // goroutine with scheduling stack
morebuf gobuf // gobuf arg to morestack
divmod uint32 // div/mod denominator for arm - known to liblink
// Fields not known to debuggers.
procid uint64 // for debuggers, but offset not hard-coded
gsignal *g // signal-handling g
goSigStack gsignalStack // Go-allocated signal handling stack
sigmask sigset // storage for saved signal mask
tls [6]uintptr // thread-local storage (for x86 extern register)
mstartfn func()
curg *g // current running goroutine
caughtsig guintptr // goroutine running during fatal signal
p puintptr // attached p for executing go code (nil if not executing go code)
nextp puintptr
oldp puintptr // the p that was attached before executing a syscall
spinning bool // m is out of work and is actively looking for work
lockedg guintptr
}
g0
是go运行时系统出之初创建的,用来执行运行时任务.mstartfn
就是编写go func携带的函数p
是当前M与哪个P关联P是G能够在M上运行的关键,P会适时的与不同M进行关联,让可运行的G及时获得M。
P的数量决定着系统中G的队列的数量,改变P的数量有两种方式:
runtime.GOMAXPROCS
进行设定P的数量上限是:256。确定P的最大数量后,运行时系统根据这个数值重新调整全局P列表。
运行时系统也包含一个空闲P的列表,当P的G队列为空的时候才会进入空闲队列
。
P本身是由状态的。
P出了有可运行的G的列表,还有自由G列表。自由G列表主要是用来存放已经运行完成的G。这个队列长度超过预期值,则会将里面的部分G转移到调度器的自由G队列。当用go原语启动一个goroutine的时候,会先从自由G列表中获取一个G,如果获取不到,会新创建G。(系统发现P的自由G太少的时候或者为空,会尝试从调度器的自由G搬运过来一些)。
运行时系统包含一个G的列表,新创建的G会第一时间添加到这里。存放当前运行时系统的全部G指针。无论G是否是新的,运行时系统都会对其进行一次初始化,关联go函数,设置G状态等。G初始化完成后,会尝试存放在P.runnext字段(该字段存在P下一个要执行的g),如果该字段已经有数值,尝试放到P的运行G的队列末尾,如果队列也满了,则放到调度器的可运行G队列中。
G的状态 :