核心元素的容器
图例
作用
3个全局容器存在的主要目的,都是为了罗列某个核心元素的全部
与G相关的调度器
与G相关的那4个非全局容器:调度器的可运行G队列、调度器的自由G队列、本地P的可运行G队列,以及本地P的自由G列表
全局G列表
任何G都会存在于全局G列表中,而其余的4个容器则只会存放在当前作用域内的、具有某个状态的G
- 这里的两个可运行G列表中的G都拥有几乎平等的运行机会
- 由于这种平等性的存在,无需关心那些可运行的G会进入哪个队列
可运行G队列
从Gsyscall状态转出的G都会被放入调度器的可运行G队列,而刚被运行时系统初始化的G都会被放入本地P的可运行G队列
至于从Gwaiting状态转出的G,有的会被放入本地P的可运行G队列,有的会被放入调度器的可运行G队列,还有的会被直接运行
此外,这两个可运行G队列之间也会互相转移G
- 例如,调用runtime.GOMAXPROCS函数,会导致运行时系统把将死的P的可运行G队列中的G,全部转移到调度器的可运行G队列。这也是为了重新分配它们
- 再如,如果本地P的可运行G队列已满,其中的一半G都会被转移到调度器的可运行G队列中
调度器的可运行G队列由两个变量代表
- 变量runqhead代表队列的头部
- runqtail则代表队列的尾部
一般情况下,新的可运行G会被追加到队列的尾部,并且已入队的G只会从头取走,这也体现了队列的FIFO(先进先出)特性
不过,新的可运行G有时候也会被插入队列头部,runtime.GOMAXPROCS函数调用就间接执行了此操作
自由G队列
另一方面,在G转入Ghead状态之后,首先会被放入本地P的自由G队列,而在运行时系统需要用自由G封装go函数的时候,也会先尝试从本地P的自由G队列中获取
如果本地P的自由G队列空了,那么运行时系统就会先从调度器的自由G队列转移一部分G到前者中
而当本地P的自由G队列已满,运行时系统也会把前者中的自由G转移一些给调度器的自由G队列
调度器的自由G列表有两个。它们的区别就是其中存放的自由G是有栈的还是无栈的
- 在把G放入自由G列表之前,运行时系统会检查该G的栈空间是否为初始大小
- 如果不是,就释放它,让该G变成无栈的,主要是为了节约资源
- 另一方面,在从自由G列表取出G之后,运行时系统会检查它是否拥有栈,如果没有就初始化一个新的栈给它
M与P的非全局容器
与M和P相关的的非全局容器分别是调度器的空闲M列表和调度器的空闲P列表,这两个列表都用于存储暂时不被使用的元素实例