p的初始化,p的状态切换

环境:

$ go version
go version go1.12.7 linux/amd64
$ uname -a
18.04.1-Ubuntu SMP x86_64 x86_64 x86_64 GNU/Linux

与分析m的流程类似,首先从new(p)语句的调用开始。通过搜索源码,能总结出一个简单的调用链:

+-------------+     +-------------------------+
| schedinit() |     | startTheWorldWithSema() |
+-----------+-+     +--+----------------------+
            |          |
            v          v
          +-+----------+-+
          | procresize() |
          +------+-------+
                 |
                 v
             +---+----+
             | new(p) |
             +--------+

其中最重要的是func procresize(nprocs int32) *p函数。这个函数的作用是改变系统中p的数量,由全局变量allp体现。主要流程如下:

值得注意的是,在减少系统中p的数量的时候,只是释放p中的资源和将状态切换为_Pdead,并没有将p置nil。

还有一点,很多博客在介绍p的时候,会将p认为是机器核的个数,通过介绍procresize函数可以看出,这是不太准确的。可以将p视作控制系统并行度的方式,因为线程只有在拿到p后才能运行,所以通过控制p的数量就能达到控制线程数量的目的,从而控制系统中线程的并行数。

可以看到在procresize函数中已经经历了p的2种状态,分别是:

p一共有5个状态,下面是状态变迁图: p状态变迁

下面是p结构体中常用的成员介绍:

type p struct {
	lock mutex
	/*
		id由机器的nprocs决定,从0~nprocs
	*/
	id     int32
	status uint32 // one of pidle/prunning/...
	link   puintptr
	/*
		if !inheritTime: _g_.m.p.ptr().schedtick++
		主要用来控制get goroutine时,从全局队列中获取的频次
	*/
	schedtick   uint32     // incremented on every scheduler call
    ...
    /*
        与p关联的m
    */
	m           muintptr   // back-link to associated m (nil if idle)
	/*
		内存分配相关
	*/
	mcache  *mcache
    ...
	// Queue of runnable goroutines. Accessed without lock.
	/*
		runqput
		runqget
		p的本地队列:
						head                        tail
		        		  |                           |
		        0		  v                           v         255
				+---+---+-+-+---+---+---+---+---+---+-+-+---+-------+
				|   |   | g | g | g | g | g | g | g |   |   |   |...|
				+---+---+---+---+---+---+---+---+---+---+---+-------+
		在runq中的goroutine处于_Grunnable状态
	*/
	runqhead uint32 /*从runqhead处取*/
	runqtail uint32 /*存入runqtail*/
	runq     [256]guintptr
    ...
	/*
		优先级比runq中的goroutine更高
	*/
	runnext guintptr

	// Available G's (status == Gdead)
	/*
		goexit0 -> gfput:
		newproc1 -> gfget:
		procresize -> gfpurge:
		gcount:

		在gFree中的goroutine处于_Gdead状态
	*/
	gFree struct {
		gList
		n int32
	}
    /*
        与channel相关
		全局使用链表,本地使用数组
	*/
	sudogcache []*sudog
	sudogbuf   [128]*sudog
    ...
}

关于p的内容,暂时就写这么多。

over:)!