岁月蹉跎敢问人生几何 發表於 2022-6-6 10:30:00

GO GMP协程调度实现原理 5w字长文史上最全

<h2 id="H0LO-1654344158396">1 Runtime简介</h2>
<div>Go语言是互联网时代的C,因为其语法简洁易学,对高并发拥有语言级别的亲和性。而且不同于虚拟机的方案。Go通过在编译时嵌入平台相关的系统指令可直接编译为对应平台的机器码,同时嵌入Go Runtime,在运行时实现自身的调度算法和各种并发控制方案,避免进入操作系统级别的进程/线程上下文切换,以及通过原子操作、自旋、信号量、全局哈希表、等待队列多种技术避免进入操作系统级别锁,以此来提升整体性能。</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604200226492-1464799632.png" alt="0" data-media-type="image"></div>
<div>Go的runtime是与用户代码一起打包在一个可执行文件中,是程序的一部分,而不是向Java需要单独安装,与程序独立。所以用户代码与runtime代码在执行时没有界限都是函数调用。在Go语言中的关键字编译时会变成runtime中的函数调用。</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604200226506-773870335.png" alt="0" data-media-type="image"></div>
<div>Go Runtime核心主要涉及三大部分:内存分配、调度算法、垃圾回收;本篇文章我们主要介绍GMP调度原理。关于具体应该叫GPM还是GMP,我更倾向于成为GMP,因为在runtime代码中经常看到如下调用:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> buf := &amp;getg().m.p.ptr().wbBuf</pre>
</div>
</div>
<div>其中getg代表获取当前正在运行的g即goroutine,m代表对应的逻辑处理器,p是逻辑调度器;所以我们还是称为GMP。</div>
<div>(以上部分图文来自:https://zhuanlan.zhihu.com/p/95056679)</div>
<div>&nbsp;</div>
<div>
<h2 id="rPlt-1654344228364">2 GMP概览</h2>
<div>下面这个图虽然有些抽象(不如花花绿绿的图片),确是目前我看到对整个调度算法设计的重要概念覆盖最全的。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span>                           +-------------------- sysmon ---------------<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">------+ </span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span>                           |                                                   |
<span style="color: rgba(0, 128, 128, 1)"> 3</span>                           |                                                   |
<span style="color: rgba(0, 128, 128, 1)"> 4</span>                +---+      +---+-------+                   +--------+          +---+---+
<span style="color: rgba(0, 128, 128, 1)"> 5</span> go func() ---&gt; | G | ---&gt; | P | local | &lt;=== balance ===&gt; | global | &lt;--<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">--- | P | M |</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>                +---+      +---+-------+                   +--------+          +---+---+
<span style="color: rgba(0, 128, 128, 1)"> 7</span>                           |                                 |               |
<span style="color: rgba(0, 128, 128, 1)"> 8</span>                           |      +---+                      |               |
<span style="color: rgba(0, 128, 128, 1)"> 9</span>                           +----&gt; | M | &lt;--- findrunnable ---+--- steal &lt;--<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">--+</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>                                    +---+
<span style="color: rgba(0, 128, 128, 1)">11</span>                                    |
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">                                 mstart
</span><span style="color: rgba(0, 128, 128, 1)">13</span>                                    |
<span style="color: rgba(0, 128, 128, 1)">14</span>               +--- execute &lt;-----<span style="color: rgba(0, 0, 0, 1)"> schedule
</span><span style="color: rgba(0, 128, 128, 1)">15</span>               |                      |   
<span style="color: rgba(0, 128, 128, 1)">16</span>               |                      |
<span style="color: rgba(0, 128, 128, 1)">17</span>               +--&gt; G.fn --&gt; goexit --+ </pre>
</div>
<p>&nbsp;</p>
</div>
<div>我们来看下其中的三大主要概念:</div>
<div>
<ul>
<li>G:Groutine协程,拥有运行函数的指针、栈、上下文(指的是sp、bp、pc等寄存器上下文以及垃圾回收的标记上下文),在整个程序运行过程中可以有无数个,代表一个用户级代码执行流(用户轻量级线程);</li>
<li>P:Processor,调度逻辑处理器,同样也是Go中代表资源的分配主体(内存资源、协程队列等),默认为机器核数,可以通过GOMAXPROCS环境变量调整</li>
<li>M:Machine,代表实际工作的执行者,对应到操作系统级别的线程;M的数量会比P多,但不会太多,最大为1w个。</li>
</ul>
</div>
<div>其中G分为三类:</div>
<div>
<ul>
<li>主协程,用来执行用户main函数的协程</li>
<li>主协程创建的协程,也是P调度的主要成员</li>
<li>G0,每个M都有一个G0协程,他是runtime的一部分,G0是跟M绑定的,主要用来执行调度逻辑的代码,所以不能被抢占也不会被调度(普通G也可以执行runtime_procPin禁止抢占),G0的栈是系统分配的,比普通的G栈(2KB)要大,不能扩容也不能缩容</li>
<li>sysmon协程,sysmon协程也是runtime的一部分,sysmon协程直接运行在M不需要P,主要做一些检查工作如:检查死锁、检查计时器获取下一个要被触发的计时任务、检查是否有ready的网络调用以恢复用户G的工作、检查一个G是否运行时间太长进行抢占式调度。</li>
</ul>
</div>
<div>M分为两类:</div>
<div>
<ul>
<li>普通M,用来与P绑定执行G中任务</li>
<li>m0:Go程序是一个进程,进程都有一个主线程,m0就是Go程序的主线程,通过一个与其绑定的G0来执行runtime启动加载代码;一个Go程序只有一个m0</li>
<li>运行sysmon的M,主要用来运行sysmon协程。</li>
</ul>
</div>
<div>&nbsp;</div>
<div>刚才说道P是用来调度G的执行,所以每个P都有自己的一个G的队列,当G队列都执行完毕后,会从global队列中获取一批G放到自己的本地队列中,如果全局队列也没有待运行的G,则P会再从其他P中窃取一部分G放到自己的队列中。而调度的时机一般有三种:</div>
<div>
<ul>
<li>主动调度,协程通过调用`runtime.Goshed`方法主动让渡自己的执行权利,之后这个协程会被放到全局队列中,等待后续被执行</li>
<li>被动调度,协程在休眠、channel通道阻塞、网络I/O堵塞、执行垃圾回收时被暂停,被动式让渡自己的执行权利。大部分场景都是被动调度,这是Go高性能的一个原因,让M永远不停歇,不处于等待的协程让出CPU资源执行其他任务。</li>
<li>抢占式调度,这个主要是sysmon协程上的调度,当发现G处于系统调用(如调用网络io)超过20微秒或者G运行时间过长(超过10ms),会抢占G的执行CPU资源,让渡给其他协程;防止其他协程没有执行的机会;(系统调用会进入内核态,由内核线程完成,可以把当前CPU资源让渡给其他用户协程)</li>
</ul>
</div>
<div>&nbsp;</div>
<div>Go的协程调度与操作系统线程调度区别主要存在四个方面:</div>
<div>
<ul>
<li>调度发生地点:Go中协程的调度发生在runtime,属于用户态,不涉及与内核态的切换;一个协程可以被切换到多个线程执行</li>
<li>上下文切换速度:协程的切换速度远快于线程,不需要经过内核与用户态切换,同时需要保存的状态和寄存器非常少;线程切换速度为1-2微秒,协程切换速度为0.2微秒左右</li>
<li>调度策略:线程调度大部分都是抢占式调度,操作系统通过发出中断信号强制线程切换上下文;Go的协程基本是主动和被动式调度,调度时机可预期</li>
<li>栈大小:线程栈一般是2MB,而且运行时不能更改大小;Go的协程栈只有2kb,而且可以动态扩容(64位机最大为1G)</li>
</ul>
</div>
<div>&nbsp;</div>
<div>以上基本是整个调度器的概括,不想看原理的同学可以不用往下看了,下面会进行源码级介绍;</div>
<div>&nbsp;</div>
</div>
<div>
<h2 id="AzWv-1654344356149">3 GMP的源码结构</h2>
<div>源码部分主要涉及三个文件:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> runtime/amd_64.s 涉及到进程启动以及对CPU执行指令进行控制的汇编代码,进程的初始化部分也在这里面
<span style="color: rgba(0, 128, 128, 1)">2</span> runtime/runtime2.go 这里主要是运行时中一些重要数据结构的定义,比如g、m、p以及涉及到接口、defer、panic、map、slice等核心类型
<span style="color: rgba(0, 128, 128, 1)">3</span> runtime/proc.go 一些核心方法的实现,涉及gmp调度等核心代码在这里</pre>
</div>
</div>
<div>这里我们主要关心gmp中与调度相关的代码;</div>
<h3 id="NDta-1654344356162">3.1 G源码部分</h3>
<h4 id="Q0Dj-1654344356164">3.1.1 G的结构</h4>
<div>先来看下g的结构定义:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime/runtime2.go</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">type g struct {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录协程栈的栈顶和栈底位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    stack       stack   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> offset known to runtime/cgo</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 主要作用是参与一些比较计算,当发现容量要超过栈分配空间后,可以进行扩容或者收缩</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    stackguard0 uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> offset known to liblink</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    stackguard1 uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> offset known to liblink</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当前与g绑定的m</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>    m         *m      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> current m; offset known to arm liblink</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这是一个比较重要的字段,里面保存的一些与goroutine运行位置相关的寄存器和指针,如rsp、rbp、rpc等寄存器</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">   sched   gobuf
</span><span style="color: rgba(0, 128, 128, 1)">13</span>    syscallsp uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> if status==Gsyscall, syscallsp = sched.sp to use during gc</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>    syscallpc uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> if status==Gsyscall, syscallpc = sched.pc to use during gc</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    stktopspuintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> expected sp at top of stack, to check in traceback</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用于做参数传递,睡眠时其他goroutine可以设置param,唤醒时该g可以读取这些param</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   param      unsafe.Pointer
</span><span style="color: rgba(0, 128, 128, 1)">19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录当前goroutine的状态</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">   atomicstatus uint32
</span><span style="color: rgba(0, 128, 128, 1)">21</span>    stackLock    uint32 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> sigprof/scang lock; TODO: fold in to atomicstatus</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goroutine的唯一id</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">   goid         int64
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">   schedlink    guintptr
</span><span style="color: rgba(0, 128, 128, 1)">25</span>   
<span style="color: rgba(0, 128, 128, 1)">26</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 标记是否可以被抢占</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>    preempt       bool <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> preemption signal, duplicates stackguard0 = stackpreempt</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>    preemptStop   bool <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> transition to _Gpreempted on preemption; otherwise, just deschedule</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>    preemptShrink bool <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> shrink stack at synchronous safe point</span>
<span style="color: rgba(0, 128, 128, 1)">30</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果调用了LockOsThread方法,则g会绑定到某个m上,只在这个m上运行</span>
<span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">   lockedm      muintptr
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">   sig            uint32
</span><span style="color: rgba(0, 128, 128, 1)">34</span>    writebuf       []<span style="color: rgba(0, 0, 255, 1)">byte</span>
<span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)">   sigcode0       uintptr
</span><span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">   sigcode1       uintptr
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">   sigpc          uintptr
</span><span style="color: rgba(0, 128, 128, 1)">38</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建该goroutine的语句的指令地址</span>
<span style="color: rgba(0, 128, 128, 1)">39</span>    gopc         uintptr         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pc of go statement that created this goroutine</span>
<span style="color: rgba(0, 128, 128, 1)">40</span>    ancestors      *[]ancestorInfo <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)</span>
<span style="color: rgba(0, 128, 128, 1)">41</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goroutine函数的指令地址</span>
<span style="color: rgba(0, 128, 128, 1)">42</span>    startpc      uintptr         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pc of goroutine function</span>
<span style="color: rgba(0, 128, 128, 1)">43</span> <span style="color: rgba(0, 0, 0, 1)">   racectx      uintptr
</span><span style="color: rgba(0, 128, 128, 1)">44</span>    waiting      *sudog         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> sudog structures this g is waiting on (that have a valid elem ptr); in lock order</span>
<span style="color: rgba(0, 128, 128, 1)">45</span>    cgoCtxt      []uintptr      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> cgo traceback context</span>
<span style="color: rgba(0, 128, 128, 1)">46</span>    labels         unsafe.Pointer <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> profiler labels</span>
<span style="color: rgba(0, 128, 128, 1)">47</span>    timer          *timer         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> cached timer for time.Sleep</span>
<span style="color: rgba(0, 128, 128, 1)">48</span>    selectDone   uint32         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> are we participating in a select and did someone win the race?</span>
<span style="color: rgba(0, 128, 128, 1)">49</span> }</pre>
</div>
<p>&nbsp;</p>
</div>
<div>跟g相关的还有两个数据结构比较重要:</div>
<div>stack是协程栈的地址信息,需要注意的是<span style="color: rgba(255, 0, 0, 1)">m0绑定的g0是在进程被分配的系统栈上分配协程栈的,而其他协程栈都是在堆上进行分配的</span>。</div>
<div>gobuf中保存了协程执行的上下文信息,这里也可以看到协程切换的上下文信息极少;sp代表cpu的rsp寄存器的值,pc代表CPU的rip寄存器值、bp代表CPU的rbp寄存器值;ret用来保存系统调用的返回值,ctxt在gc的时候使用。</div>
<div>其中几个寄存器作用如下:</div>
<div>
<ul>
<li>SP:永远指向栈顶位置</li>
<li>BP:某一时刻的栈顶位置,当新函数调用时,把当前SP地址赋值给BP、SP指向新的栈顶位置</li>
<li>PC:代表代码经过编译为机器码后,当前执行的机器指令(可以理解为当前语句)</li>
</ul>
</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Stack describes a Go execution stack.</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The bounds of the stack are exactly [lo, hi),</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> with no implicit data structures on either side.</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goroutine协程栈的栈顶和栈底</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">type stack struct {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>    lo uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 栈顶,低地址</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    hi uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 栈底,高地址</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gobuf中保存了非常重要的上下文执行信息,</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">type gobuf struct {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表cpu的rsp寄存器的值,永远指向栈顶位置</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">   sp   uintptr
</span><span style="color: rgba(0, 128, 128, 1)">14</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表代码经过编译为机器码后,当前执行的机器指令(可以理解为当前语句)</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">   pc   uintptr
</span><span style="color: rgba(0, 128, 128, 1)">16</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指向所保存执行上下文的goroutine</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">   g    guintptr
</span><span style="color: rgba(0, 128, 128, 1)">18</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gc时候使用</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">   ctxt unsafe.Pointer
</span><span style="color: rgba(0, 128, 128, 1)">20</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用来保存系统调用的返回值</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">   retuintptr
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">   lr   uintptr
</span><span style="color: rgba(0, 128, 128, 1)">23</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 某一时刻的栈顶位置,当新函数调用时,把当前SP地址赋值给BP、SP指向新的栈顶位置</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>    bp   uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> for framepointer-enabled architectures</span>
<span style="color: rgba(0, 128, 128, 1)">25</span> }</pre>
</div>
<p>&nbsp;</p>
</div>
<h4 id="nu5s-1654344356336">3.1.2 G的状态</h4>
<div>就像线程有自己的状态一样,goroutine也有自己的状态,主要记录在atomicstatus字段上:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> defined constants</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">const (
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表协程刚开始创建时的状态,当新创建的协程初始化后,为变为_Gdead状态,_Gdread也是协程被销毁时的状态;</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 刚创建时也被会置为_Gdead主要是考虑GC可以去用去扫描dead状态下的协程栈</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    _Gidle = iota <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表协程正在运行队列中,等待被运行</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    _Grunnable <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 1</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表当前协程正在被运行,已经被分配了逻辑处理的线程,即p和m</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    _Grunning <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表当前协程正在执行系统调用</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    _Gsyscall <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 3</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表示当前协程在运行时被锁定,陷入阻塞,不能执行用户代码</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>    _Gwaiting <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    _Gmoribund_unused <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 5</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 新创建的协程初始化后,或者协程被销毁后的状态</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    _Gdead <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 6</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> _Genqueue_unused is currently unused.</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>    _Genqueue_unused <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 7</span>
<span style="color: rgba(0, 128, 128, 1)">21</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表在进行协程栈扫描时发现需要扩容或者缩容,将协程中的栈转移到新栈时的状态;这个时候不执行用户代码,也不在p的runq中</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>    _Gcopystack <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表g被抢占后的状态</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    _Gpreempted <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 9</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这几个状态是垃圾回收时涉及,后续文章进行介绍</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>    _Gscan          = 0x1000
<span style="color: rgba(0, 128, 128, 1)">29</span>    _Gscanrunnable= _Gscan + _Grunnable<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0x1001</span>
<span style="color: rgba(0, 128, 128, 1)">30</span>    _Gscanrunning   = _Gscan + _Grunning   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0x1002</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>    _Gscansyscall   = _Gscan + _Gsyscall   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0x1003</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>    _Gscanwaiting   = _Gscan + _Gwaiting   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0x1004</span>
<span style="color: rgba(0, 128, 128, 1)">33</span>    _Gscanpreempted = _Gscan + _Gpreempted <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0x1009</span>
<span style="color: rgba(0, 128, 128, 1)">34</span> )</pre>
</div>
<p>&nbsp;</p>
</div>
<div>这里是利用常量定义的枚举。</div>
<div>Go的状态变更可以看下图:</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604200634151-1066411049.png" alt="0" data-media-type="image"></div>
<div>&nbsp;</div>
<h4>&nbsp;<span style="font-size: 18px; font-weight: bold; white-space: pre-wrap">3.1.3 G的创建</span></h4>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">当我们使用go关键字新建一个goroutine时,编译器会编译为runtime中对应的函数调用(newproc,而</span><span style="font-size: inherit; font-weight: bold; color: rgba(64, 62, 214, 1)">go 关键字后面的函数成为协程的任务函数</span><span style="font-size: inherit">),进行创建,整体步骤如下:</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">1. 用&nbsp;systemstack&nbsp;切换到系统堆栈,调用&nbsp;newproc1&nbsp;,newproc1&nbsp;实现g的获取。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">2. 尝试从p的本地g空闲链表和全局g空闲链表找到一个g的实例。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">3. 如果上面未找到,则调用&nbsp;malg&nbsp;生成新的g的实例,且分配好g的栈和设置好栈的边界,接着添加到 allgs 数组里面,allgs保存了所有的g。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">4. 保存g切换的上下文,这里很关键,g的切换依赖&nbsp;sched&nbsp;字段。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">5. 生成唯一的goid,赋值给该g。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">6. 调用&nbsp;runqput&nbsp;将g插入队列中,如果本地队列还有剩余的位置,将G插入本地队列的尾部,若本地队列已满,插入全局队列。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">7. 如果有空闲的p 且 m没有处于自旋状态 且&nbsp;main goroutine已经启动,那么唤醒或新建某个m来执行任务。</span></div>
<div style="white-space: pre-wrap; line-height: 1.75">&nbsp;</div>
<div style="white-space: pre-wrap; line-height: 1.75">&nbsp;</div>
<div>&nbsp;<span style="font-size: inherit; white-space: pre-wrap">这里对应的是newproc函数:</span></div>
<div style="white-space: pre-wrap" data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func newproc(siz int32, fn *<span style="color: rgba(0, 0, 0, 1)">funcval) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    argp := add(unsafe.Pointer(&amp;<span style="color: rgba(0, 0, 0, 1)">fn), sys.PtrSize)
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    gp :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取调用者的指令地址,也就是调用newproc时又call指令压栈的函数返回地址</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    pc :=<span style="color: rgba(0, 0, 0, 1)"> getcallerpc()
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> systemstack的作用是切换到m0对应的g0所属的系统栈</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用g0所在的系统栈创建goroutine对象</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 传递参数包括goroutine的任务函数、argp参数起始地址、siz是参数长度、调用方的pc指针</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   systemstack(func() {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       newg :=<span style="color: rgba(0, 0, 0, 1)"> newproc1(fn, argp, siz, gp, pc)
</span><span style="color: rgba(0, 128, 128, 1)">11</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建完成后将g放到创建者(某个g,如果是进程初始化启动阶段则为g0)所在的p的队列中</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>       _p_ :=<span style="color: rgba(0, 0, 0, 1)"> getg().m.p.ptr()
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       runqput(_p_, newg, <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>       <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> mainStarted {
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">         wakep()
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   })
</span><span style="color: rgba(0, 128, 128, 1)">19</span> }</pre>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">其中systemstack是一段汇编代码,位于asm_amd64.s文件中,主要是寄存器指令的操作,笔者不懂汇编这里先不做介绍。</span></div>
<div style="line-height: 1.75">&nbsp;</div>
<div style="line-height: 1.75"><span style="font-size: inherit">newproc1是获取newg的函数,主要步骤:</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">1、首先防止当前g被抢占,绑定m</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">2、对传入的参数占用的内存空间进行对齐处理</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">3、从p的空闲队列中获取一个空闲的g,如果么有就创建一个g,并在堆上创建协程栈,并设置状态为_Gdead添加到全局allgs中</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">4、计算整体协程任务函数的参数空间大小,并设置sp指针</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">5、执行参数从getg的堆栈到newg堆栈的复制</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">6、设置newg的sched和startpc、gopc等跟上下文相关的字段值</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">7、设置newg状态为runable并设置goid</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">8、接触getg与m的防抢占状态</span></div>
</div>
</div>
<div>&nbsp;</div>
<div>&nbsp;<span style="font-size: inherit; white-space: pre-wrap">代码注释如下:</span></div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> func newproc1(fn *funcval, argp unsafe.Pointer, narg int32, callergp *g, callerpc uintptr) *<span style="color: rgba(0, 0, 0, 1)">g {
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">    .....
</span><span style="color: rgba(0, 128, 128, 1)">3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果是初始化时候这个代表g0</span>
<span style="color: rgba(0, 128, 128, 1)">4</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)">5</span>
<span style="color: rgba(0, 128, 128, 1)">6</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> fn ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">7</span>       _g_.m.throwing = -1 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> do not dump full stacks</span>
<span style="color: rgba(0, 128, 128, 1)">8</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("go of nil func value"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">9</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 10</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使_g_.m.locks++,来防止这个时候g对应的m被抢占</span>
<span style="color: rgba(0, 128, 128, 1)"> 11</span>    acquirem() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> disable preemption because it can be holding p in a local var</span>
<span style="color: rgba(0, 128, 128, 1)"> 12</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 参数的地址,下面一句目的是为了做到内存对齐</span>
<span style="color: rgba(0, 128, 128, 1)"> 13</span>    siz :=<span style="color: rgba(0, 0, 0, 1)"> narg
</span><span style="color: rgba(0, 128, 128, 1)"> 14</span>    siz = (siz + 7) &amp;^ 7
<span style="color: rgba(0, 128, 128, 1)"> 15</span>
<span style="color: rgba(0, 128, 128, 1)"> 16</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We could allocate a larger initial stack if necessary.</span>
<span style="color: rgba(0, 128, 128, 1)"> 17</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Not worth it: this is almost always an error.</span>
<span style="color: rgba(0, 128, 128, 1)"> 18</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 4*PtrSize: extra space added below</span>
<span style="color: rgba(0, 128, 128, 1)"> 19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> PtrSize: caller's LR (arm) or return address (x86, in gostartcall).</span>
<span style="color: rgba(0, 128, 128, 1)"> 20</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> siz &gt;= _StackMin-4*sys.PtrSize-<span style="color: rgba(0, 0, 0, 1)">sys.PtrSize {
</span><span style="color: rgba(0, 128, 128, 1)"> 21</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("newproc: function arguments too large for new goroutine"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 22</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 23</span>
<span style="color: rgba(0, 128, 128, 1)"> 24</span>    _p_ :=<span style="color: rgba(0, 0, 0, 1)"> _g_.m.p.ptr()
</span><span style="color: rgba(0, 128, 128, 1)"> 25</span>    newg := gfget(_p_) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 首先从p的gfree队列中看看有没有空闲的g,有则使用</span>
<span style="color: rgba(0, 128, 128, 1)"> 26</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> newg ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)"> 27</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果没找到就使用new关键字来创建一个g并在堆上分配栈空间</span>
<span style="color: rgba(0, 128, 128, 1)"> 28</span>       newg =<span style="color: rgba(0, 0, 0, 1)"> malg(_StackMin)
</span><span style="color: rgba(0, 128, 128, 1)"> 29</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将newg的状态设置为_Gdead,因为这样GC就不会去扫描一个没有初始化的协程栈</span>
<span style="color: rgba(0, 128, 128, 1)"> 30</span> <span style="color: rgba(0, 0, 0, 1)">      casgstatus(newg, _Gidle, _Gdead)
</span><span style="color: rgba(0, 128, 128, 1)"> 31</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 添加到全局的allg切片中(需要加锁访问)</span>
<span style="color: rgba(0, 128, 128, 1)"> 32</span>       allgadd(newg) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> publishes with a g-&gt;status of Gdead so GC scanner doesn't look at uninitialized stack.</span>
<span style="color: rgba(0, 128, 128, 1)"> 33</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 34</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 下面是检查协程栈的创建情况和状态</span>
<span style="color: rgba(0, 128, 128, 1)"> 35</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> newg.stack.hi == 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 36</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("newproc1: newg missing stack"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 37</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 38</span>
<span style="color: rgba(0, 128, 128, 1)"> 39</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> readgstatus(newg) !=<span style="color: rgba(0, 0, 0, 1)"> _Gdead {
</span><span style="color: rgba(0, 128, 128, 1)"> 40</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("newproc1: new g is not Gdead"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 41</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 42</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 计算运行空间大小并进行内存对齐</span>
<span style="color: rgba(0, 128, 128, 1)"> 43</span>    totalSize := 4*sys.PtrSize + uintptr(siz) + sys.MinFrameSize <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> extra space in case of reads slightly beyond frame</span>
<span style="color: rgba(0, 128, 128, 1)"> 44</span>    totalSize += -totalSize &amp; (sys.StackAlign - 1)               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> align to StackAlign</span>
<span style="color: rgba(0, 128, 128, 1)"> 45</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 计算sp寄存器指针的位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 46</span>    sp := newg.stack.hi -<span style="color: rgba(0, 0, 0, 1)"> totalSize
</span><span style="color: rgba(0, 128, 128, 1)"> 47</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 确定参数入栈位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 48</span>    spArg :=<span style="color: rgba(0, 0, 0, 1)"> sp
</span><span style="color: rgba(0, 128, 128, 1)"> 49</span> <span style="color: rgba(0, 0, 0, 1)">   .........
</span><span style="color: rgba(0, 128, 128, 1)"> 50</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> narg &gt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 51</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将参数从newproc函数的栈复制到新的协程的栈中,memove是一段汇编代码</span>
<span style="color: rgba(0, 128, 128, 1)"> 52</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从argp位置挪动narg大小的内存到sparg位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 53</span> <span style="color: rgba(0, 0, 0, 1)">      memmove(unsafe.Pointer(spArg), argp, uintptr(narg))
</span><span style="color: rgba(0, 128, 128, 1)"> 54</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 因为涉及到从栈到堆栈上的复制,go在垃圾回收中使用了三色标记和写入屏障等手段,所以这里要考虑屏障复制</span>
<span style="color: rgba(0, 128, 128, 1)"> 55</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 目标栈可能会有垃圾存在,所以设置屏障并且标记为灰色</span>
<span style="color: rgba(0, 128, 128, 1)"> 56</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> writeBarrier.needed &amp;&amp; !_g_.m.curg.gcscandone { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果启用了写入屏障并且源堆栈为灰色(目标始终为黑色),则执行屏障复制。</span>
<span style="color: rgba(0, 128, 128, 1)"> 57</span>          f :=<span style="color: rgba(0, 0, 0, 1)"> findfunc(fn.fn)
</span><span style="color: rgba(0, 128, 128, 1)"> 58</span>          stkmap := (*<span style="color: rgba(0, 0, 0, 1)">stackmap)(funcdata(f, _FUNCDATA_ArgsPointerMaps))
</span><span style="color: rgba(0, 128, 128, 1)"> 59</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> stkmap.nbit &gt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 60</span>             <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> We're in the prologue, so it's always stack map index 0.</span>
<span style="color: rgba(0, 128, 128, 1)"> 61</span>             bv := stackmapdata(stkmap, 0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 62</span>             bulkBarrierBitmap(spArg, spArg, uintptr(bv.n)*sys.PtrSize, 0<span style="color: rgba(0, 0, 0, 1)">, bv.bytedata)
</span><span style="color: rgba(0, 128, 128, 1)"> 63</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)"> 64</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)"> 65</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 66</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 把newg的sched结构体成员的所有字段都设置为0,其实就是初始化</span>
<span style="color: rgba(0, 128, 128, 1)"> 67</span>    memclrNoHeapPointers(unsafe.Pointer(&amp;<span style="color: rgba(0, 0, 0, 1)">newg.sched), unsafe.Sizeof(newg.sched))
</span><span style="color: rgba(0, 128, 128, 1)"> 68</span>    newg.sched.sp =<span style="color: rgba(0, 0, 0, 1)"> sp
</span><span style="color: rgba(0, 128, 128, 1)"> 69</span>    newg.stktopsp =<span style="color: rgba(0, 0, 0, 1)"> sp
</span><span style="color: rgba(0, 128, 128, 1)"> 70</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pc指针表示当newg被调度起来时从这个位置开始执行</span>
<span style="color: rgba(0, 128, 128, 1)"> 71</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里是先设置为goexit,在gostartcallfn中会进行处理,更改sp为这里的pc,将pc改为真正的协程任务函数fn的指令位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 72</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这样使得任务函数执行完毕后,会继续执行goexit中相关的清理工作</span>
<span style="color: rgba(0, 128, 128, 1)"> 73</span>    newg.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> +PCQuantum so that previous instruction is in same function</span>
<span style="color: rgba(0, 128, 128, 1)"> 74</span>    newg.sched.g = guintptr(unsafe.Pointer(newg)) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存当前的g</span>
<span style="color: rgba(0, 128, 128, 1)"> 75</span>    gostartcallfn(&amp;newg.sched, fn) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在这里完成g启动时所有相关上下文指针的设置,主要为sp、pc和ctxt,ctxt被设置为fn</span>
<span style="color: rgba(0, 128, 128, 1)"> 76</span>    newg.gopc = callerpc <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存newproc的pc,即调用者创建时的指令位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 77</span>    newg.ancestors =<span style="color: rgba(0, 0, 0, 1)"> saveAncestors(callergp)
</span><span style="color: rgba(0, 128, 128, 1)"> 78</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置startpc为任务函数,主要用于函数调用栈的trackback和栈收缩工作</span>
<span style="color: rgba(0, 128, 128, 1)"> 79</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> newg的执行开始位置并不依赖这个字段,而是通过sched.pc确定</span>
<span style="color: rgba(0, 128, 128, 1)"> 80</span>    newg.startpc =<span style="color: rgba(0, 0, 0, 1)"> fn.fn
</span><span style="color: rgba(0, 128, 128, 1)"> 81</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m.curg !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)"> 82</span>       newg.labels =<span style="color: rgba(0, 0, 0, 1)"> _g_.m.curg.labels
</span><span style="color: rgba(0, 128, 128, 1)"> 83</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 84</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 判断newg的任务函数是不是runtime系统的任务函数,是则sched.ngsys+1;</span>
<span style="color: rgba(0, 128, 128, 1)"> 85</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 主协程则代表runtime.main函数,在这里就为判断为真</span>
<span style="color: rgba(0, 128, 128, 1)"> 86</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> isSystemGoroutine(newg, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 87</span>       atomic.Xadd(&amp;sched.ngsys, +1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 88</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 89</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Track initial transition?</span>
<span style="color: rgba(0, 128, 128, 1)"> 90</span>    newg.trackingSeq =<span style="color: rgba(0, 0, 0, 1)"> uint8(fastrand())
</span><span style="color: rgba(0, 128, 128, 1)"> 91</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> newg.trackingSeq%gTrackingPeriod == 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 92</span>       newg.tracking = <span style="color: rgba(0, 0, 255, 1)">true</span>
<span style="color: rgba(0, 128, 128, 1)"> 93</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 94</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更改当前g的状态为_Grunnable</span>
<span style="color: rgba(0, 128, 128, 1)"> 95</span> <span style="color: rgba(0, 0, 0, 1)">   casgstatus(newg, _Gdead, _Grunnable)
</span><span style="color: rgba(0, 128, 128, 1)"> 96</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置g的goid,因为p会每次批量生成16个id,每次newproc如果新建一个g,id就会加1</span>
<span style="color: rgba(0, 128, 128, 1)"> 97</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 所以这里m0的g0的id为0,而主协程的goid为1,其他的依次递增</span>
<span style="color: rgba(0, 128, 128, 1)"> 98</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _p_.goidcache ==<span style="color: rgba(0, 0, 0, 1)"> _p_.goidcacheend {
</span><span style="color: rgba(0, 128, 128, 1)"> 99</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Sched.goidgen is the last allocated id,</span>
<span style="color: rgba(0, 128, 128, 1)">100</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this batch must be .</span>
<span style="color: rgba(0, 128, 128, 1)">101</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> At startup sched.goidgen=0, so main goroutine receives goid=1.</span>
<span style="color: rgba(0, 128, 128, 1)">102</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使用原子操作修改全局变量,这里的sched是在runtime2.go中的一个全局变量类型为schedt</span>
<span style="color: rgba(0, 128, 128, 1)">103</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 原子操作具有多线程可见性,同时比加锁性能更高</span>
<span style="color: rgba(0, 128, 128, 1)">104</span>       _p_.goidcache = atomic.Xadd64(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.goidgen, _GoidCacheBatch)
</span><span style="color: rgba(0, 128, 128, 1)">105</span>       _p_.goidcache -= _GoidCacheBatch - 1
<span style="color: rgba(0, 128, 128, 1)">106</span>       _p_.goidcacheend = _p_.goidcache +<span style="color: rgba(0, 0, 0, 1)"> _GoidCacheBatch
</span><span style="color: rgba(0, 128, 128, 1)">107</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">108</span>    newg.goid =<span style="color: rgba(0, 0, 0, 1)"> int64(_p_.goidcache)
</span><span style="color: rgba(0, 128, 128, 1)">109</span>    _p_.goidcache++
<span style="color: rgba(0, 128, 128, 1)">110</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> raceenabled {
</span><span style="color: rgba(0, 128, 128, 1)">111</span>       newg.racectx =<span style="color: rgba(0, 0, 0, 1)"> racegostart(callerpc)
</span><span style="color: rgba(0, 128, 128, 1)">112</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">113</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> trace.enabled {
</span><span style="color: rgba(0, 128, 128, 1)">114</span> <span style="color: rgba(0, 0, 0, 1)">      traceGoCreate(newg, newg.startpc)
</span><span style="color: rgba(0, 128, 128, 1)">115</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">116</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 释放getg与m的绑定</span>
<span style="color: rgba(0, 128, 128, 1)">117</span> <span style="color: rgba(0, 0, 0, 1)">   releasem(_g_.m)
</span><span style="color: rgba(0, 128, 128, 1)">118</span>
<span style="color: rgba(0, 128, 128, 1)">119</span>    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> newg
</span><span style="color: rgba(0, 128, 128, 1)">120</span> }</pre>
</div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">其中有几个关键地方需要强调</span></div>
<div style="white-space: pre-wrap; line-height: 1.75">
<h4 id="q598-1654344739739" style="line-height: 1.75"><span style="font-size: 18px">3.1.4 协程栈在堆空间的分配</span></h4>
<div style="line-height: 1.75"><span style="font-size: inherit">malg函数,用来创建一个新g和对应的栈空间分配,这个函数主要强调的是栈空间分配部分,通过切换到系统栈上进行空间分配,分配完后设置栈底和栈顶的两个位置的保护字段,当栈上进行分配变量空间发现超过stackguard1时,会进行扩容,同时在某些条件下也会进行缩容</span></div>
</div>
</div>
<div>&nbsp;
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Allocate a new g, with a stack big enough for stacksize bytes.</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> func malg(stacksize int32) *<span style="color: rgba(0, 0, 0, 1)">g {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    newg := <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">(g)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> stacksize &gt;= 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>       stacksize = round2(_StackSystem +<span style="color: rgba(0, 0, 0, 1)"> stacksize)
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">      systemstack(func() {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>          newg.stack =<span style="color: rgba(0, 0, 0, 1)"> stackalloc(uint32(stacksize))
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      })
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>       newg.stackguard0 = newg.stack.lo +<span style="color: rgba(0, 0, 0, 1)"> _StackGuard
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       newg.stackguard1 = ^uintptr(0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">11</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Clear the bottom word of the stack. We record g</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> there on gsignal stack during VDSO on ARM and ARM64.</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>       *(*uintptr)(unsafe.Pointer(newg.stack.lo)) = 0
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">15</span>    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> newg
</span><span style="color: rgba(0, 128, 128, 1)">16</span> }</pre>
</div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">stackalloc代码位于runtime/stack.go文件中;</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">协程栈首先在进程初始化时会创建栈的管理结构:</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">1、栈池stackpool,这个栈池主要用来对大小为2、4、8kb的小栈做缓存使用,使用的同样是mspan这种结构来存储;</span></div>
<div style="white-space: pre-wrap; line-height: 1.75"><span style="font-size: inherit">2、为大栈分配的stackLarge</span></div>
<div style="white-space: pre-wrap; line-height: 1.75">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span>   OS               | FixedStack |<span style="color: rgba(0, 0, 0, 1)"> NumStackOrders
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   -----------------+------------+---------------
<span style="color: rgba(0, 128, 128, 1)"> 3</span>   linux/darwin/bsd | 2KB      | 4
<span style="color: rgba(0, 128, 128, 1)"> 4</span>   windows/32       | 4KB      | 3
<span style="color: rgba(0, 128, 128, 1)"> 5</span>   windows/64       | 8KB      | 2
<span style="color: rgba(0, 128, 128, 1)"> 6</span>   plan9            | 4KB      | 3
<span style="color: rgba(0, 128, 128, 1)"> 7</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Global pool of spans that have free stacks.</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Stacks are assigned an order according to size.</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">   order = log_2(size/FixedStack)</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> There is a free list for each order.</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> stackpool struct {
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">   item stackpoolItem
</span><span style="color: rgba(0, 128, 128, 1)">14</span>    _    <span style="color: rgba(0, 0, 255, 1)">byte</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">16</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">go:notinheap</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">type stackpoolItem struct {
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">   mu   mutex
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">   span mSpanList
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">22</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Global pool of large stack spans.</span>
<span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> stackLarge struct {
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">   lock mutex
</span><span style="color: rgba(0, 128, 128, 1)">26</span>    free mSpanList <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> free lists by log_2(s.npages)</span>
<span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">28</span>
<span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">func stackinit() {
</span><span style="color: rgba(0, 128, 128, 1)">30</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _StackCacheSize&amp;_PageMask != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">31</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("cache size must be a multiple of page size"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">33</span>    <span style="color: rgba(0, 0, 255, 1)">for</span> i :=<span style="color: rgba(0, 0, 0, 1)"> range stackpool {
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">      stackpool.item.span.init()
</span><span style="color: rgba(0, 128, 128, 1)">35</span>       lockInit(&amp;<span style="color: rgba(0, 0, 0, 1)">stackpool.item.mu, lockRankStackpool)
</span><span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">37</span>    <span style="color: rgba(0, 0, 255, 1)">for</span> i :=<span style="color: rgba(0, 0, 0, 1)"> range stackLarge.free {
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">      stackLarge.free.init()
</span><span style="color: rgba(0, 128, 128, 1)">39</span>       lockInit(&amp;<span style="color: rgba(0, 0, 0, 1)">stackLarge.lock, lockRankStackLarge)
</span><span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">41</span> }</pre>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">stackalloc会首先判断栈空间大小,是大栈还是固定空间的小栈,</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">1、对于小栈,如果是还没有分配栈缓存空间,则进入stackpoolalloc函数进行分配空间(需要加锁),这里最终是从全局的mheap也就是堆空间中获取内存空间;如果有栈缓存空间,则从g对应的mcache中的stackcache上获取内存空间(无锁),如果stackcache上没有足够空间则调用stackcacherefill方法为stackpool进行扩容(也是从mheap中拿取,加锁)然后分配给协程</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">2、对于大栈,先从stackLarge中获取,如果没有则从mheap中获取,两个步骤都需要加载访问;</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">3、最后创建stack结构返回给newg</span></div>
<div style="line-height: 1.75">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func stackalloc(n uint32) stack {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Stackalloc must be called on scheduler stack, so that we</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> never try to grow the stack during the code that stackalloc runs.</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Doing so would cause a deadlock (issue 1547).</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    thisg :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">.........
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Small stacks are allocated with a fixed-size free-list allocator.</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> If we need a stack of a bigger size, we fall back on allocating</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> a dedicated span.</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> v unsafe.Pointer
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> n &lt; _FixedStack&lt;&lt;_NumStackOrders &amp;&amp; n &lt;<span style="color: rgba(0, 0, 0, 1)"> _StackCacheSize {
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       order := uint8(0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">14</span>       n2 :=<span style="color: rgba(0, 0, 0, 1)"> n
</span><span style="color: rgba(0, 128, 128, 1)">15</span>       <span style="color: rgba(0, 0, 255, 1)">for</span> n2 &gt;<span style="color: rgba(0, 0, 0, 1)"> _FixedStack {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>          order++
<span style="color: rgba(0, 128, 128, 1)">17</span>          n2 &gt;&gt;= 1
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">19</span>       <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> x gclinkptr
</span><span style="color: rgba(0, 128, 128, 1)">20</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> stackNoCache != 0 || thisg.m.p == 0 || thisg.m.preemptoff != ""<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">21</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> thisg.m.p == 0 can happen in the guts of exitsyscall</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> or procresize. Just get a stack from the global pool.</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Also don't touch stackcache during gc</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> as it's flushed concurrently.</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>          lock(&amp;<span style="color: rgba(0, 0, 0, 1)">stackpool.item.mu)
</span><span style="color: rgba(0, 128, 128, 1)">26</span>          x =<span style="color: rgba(0, 0, 0, 1)"> stackpoolalloc(order)
</span><span style="color: rgba(0, 128, 128, 1)">27</span>          unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">stackpool.item.mu)
</span><span style="color: rgba(0, 128, 128, 1)">28</span>       } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">29</span>          c :=<span style="color: rgba(0, 0, 0, 1)"> thisg.m.p.ptr().mcache
</span><span style="color: rgba(0, 128, 128, 1)">30</span>          x =<span style="color: rgba(0, 0, 0, 1)"> c.stackcache.list
</span><span style="color: rgba(0, 128, 128, 1)">31</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> x.ptr() ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">            stackcacherefill(c, order)
</span><span style="color: rgba(0, 128, 128, 1)">33</span>             x =<span style="color: rgba(0, 0, 0, 1)"> c.stackcache.list
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">35</span>          c.stackcache.list =<span style="color: rgba(0, 0, 0, 1)"> x.ptr().next
</span><span style="color: rgba(0, 128, 128, 1)">36</span>          c.stackcache.size -=<span style="color: rgba(0, 0, 0, 1)"> uintptr(n)
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">38</span>       v =<span style="color: rgba(0, 0, 0, 1)"> unsafe.Pointer(x)
</span><span style="color: rgba(0, 128, 128, 1)">39</span>    } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">40</span>       <span style="color: rgba(0, 0, 255, 1)">var</span> s *<span style="color: rgba(0, 0, 0, 1)">mspan
</span><span style="color: rgba(0, 128, 128, 1)">41</span>       npage := uintptr(n) &gt;&gt;<span style="color: rgba(0, 0, 0, 1)"> _PageShift
</span><span style="color: rgba(0, 128, 128, 1)">42</span>       log2npage :=<span style="color: rgba(0, 0, 0, 1)"> stacklog2(npage)
</span><span style="color: rgba(0, 128, 128, 1)">43</span>
<span style="color: rgba(0, 128, 128, 1)">44</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Try to get a stack from the large stack cache.</span>
<span style="color: rgba(0, 128, 128, 1)">45</span>       lock(&amp;<span style="color: rgba(0, 0, 0, 1)">stackLarge.lock)
</span><span style="color: rgba(0, 128, 128, 1)">46</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> !<span style="color: rgba(0, 0, 0, 1)">stackLarge.free.isEmpty() {
</span><span style="color: rgba(0, 128, 128, 1)">47</span>          s =<span style="color: rgba(0, 0, 0, 1)"> stackLarge.free.first
</span><span style="color: rgba(0, 128, 128, 1)">48</span> <span style="color: rgba(0, 0, 0, 1)">         stackLarge.free.remove(s)
</span><span style="color: rgba(0, 128, 128, 1)">49</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">50</span>       unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">stackLarge.lock)
</span><span style="color: rgba(0, 128, 128, 1)">51</span>
<span style="color: rgba(0, 128, 128, 1)">52</span>       lockWithRankMayAcquire(&amp;<span style="color: rgba(0, 0, 0, 1)">mheap_.lock, lockRankMheap)
</span><span style="color: rgba(0, 128, 128, 1)">53</span>
<span style="color: rgba(0, 128, 128, 1)">54</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> s ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">55</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Allocate a new stack from the heap.</span>
<span style="color: rgba(0, 128, 128, 1)">56</span>          s =<span style="color: rgba(0, 0, 0, 1)"> mheap_.allocManual(npage, spanAllocStack)
</span><span style="color: rgba(0, 128, 128, 1)">57</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> s ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">58</span>             <span style="color: rgba(0, 0, 255, 1)">throw</span>("out of memory"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">59</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">60</span> <span style="color: rgba(0, 0, 0, 1)">         osStackAlloc(s)
</span><span style="color: rgba(0, 128, 128, 1)">61</span>          s.elemsize =<span style="color: rgba(0, 0, 0, 1)"> uintptr(n)
</span><span style="color: rgba(0, 128, 128, 1)">62</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">63</span>       v =<span style="color: rgba(0, 0, 0, 1)"> unsafe.Pointer(s.base())
</span><span style="color: rgba(0, 128, 128, 1)">64</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">65</span>
<span style="color: rgba(0, 128, 128, 1)">66</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> raceenabled {
</span><span style="color: rgba(0, 128, 128, 1)">67</span> <span style="color: rgba(0, 0, 0, 1)">      racemalloc(v, uintptr(n))
</span><span style="color: rgba(0, 128, 128, 1)">68</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">69</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> msanenabled {
</span><span style="color: rgba(0, 128, 128, 1)">70</span> <span style="color: rgba(0, 0, 0, 1)">      msanmalloc(v, uintptr(n))
</span><span style="color: rgba(0, 128, 128, 1)">71</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">72</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> stackDebug &gt;= 1<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">73</span>       print("allocated ", v, "\n"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">74</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">75</span>    <span style="color: rgba(0, 0, 255, 1)">return</span> stack{uintptr(v), uintptr(v) +<span style="color: rgba(0, 0, 0, 1)"> uintptr(n)}
</span><span style="color: rgba(0, 128, 128, 1)">76</span> }</pre>
</div>
<div style="line-height: 1.75">&nbsp;</div>
<div style="line-height: 1.75"><strong><span style="font-size: inherit">非g0的g为什么要在堆上分配空间?</span></strong></div>
<div style="line-height: 1.75"><span style="font-size: inherit">虽然堆不如栈快,但是goroutine是go模拟的线程,具有动态扩容和缩容的能力,而系统栈是线性空间,在系统栈上发生缩容和扩容会存在空间不足或者栈空间碎片等问题;所以go这里在堆上分配协程栈;因为是在堆空间也就意味着这部分空间也需要进行垃圾回收和释放;所以Go的GC是多线程并发标记时,内存屏障是对整个协程栈标记灰色,来让回收器进行扫描。</span></div>
</div>
<div style="line-height: 1.75">
<h4 id="1VHF-1654344882674" style="line-height: 1.75"><span style="font-size: 18px">3.1.5 G的上下文设置和切换</span></h4>
<div style="line-height: 1.75"><span style="font-size: inherit">协程栈的切换主要是在两个地方,由执行调度逻辑的g0切换到执行用户逻辑的g的过程,以及执行用户逻辑的g退出或者被抢占切换为g0执行调度的过程,抢占在下文中介绍</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">上面代码中当newg被初始化时,会初始化sched中的pc和sp指针,其中会把pc先设置为goexit函数的第二条指令。</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 把newg的sched结构体成员的所有字段都设置为0,其实就是初始化</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span>    memclrNoHeapPointers(unsafe.Pointer(&amp;<span style="color: rgba(0, 0, 0, 1)">newg.sched), unsafe.Sizeof(newg.sched))
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    newg.sched.sp =<span style="color: rgba(0, 0, 0, 1)"> sp
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    newg.stktopsp =<span style="color: rgba(0, 0, 0, 1)"> sp
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pc指针表示当newg被调度起来时从这个位置开始执行</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里是先设置为goexit,在gostartcallfn中会进行处理,更改sp为这里的pc,将pc改为真正的协程任务函数fn的指令位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这样使得任务函数执行完毕后,会继续执行goexit中相关的清理工作</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    newg.sched.pc = abi.FuncPCABI0(goexit) + sys.PCQuantum <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> +PCQuantum so that previous instruction is in same function</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    newg.sched.g = guintptr(unsafe.Pointer(newg)) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存当前的g</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>    gostartcallfn(&amp;newg.sched, fn) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在这里完成g启动时所有相关上下文指针的设置,主要为sp、pc和ctxt,ctxt被设置为fn</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    newg.gopc = callerpc <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存newproc的pc,即调用者创建时的指令位置</span></pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">然后进入gostartcallfn函数,最终是在gostartcall函数中进行处理</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gostartcallfn 位于runtime/stack.go中</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> func gostartcallfn(gobuf *gobuf, fv *<span style="color: rgba(0, 0, 0, 1)">funcval) {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> fn unsafe.Pointer
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> fv !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       fn =<span style="color: rgba(0, 0, 0, 1)"> unsafe.Pointer(fv.fn)
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       fn =<span style="color: rgba(0, 0, 0, 1)"> unsafe.Pointer(funcPC(nilfunc))
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">   gostartcall(gobuf, fn, unsafe.Pointer(fv))
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">12</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gostartcall 位于runtime/sys_x86.go中</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> func gostartcall(buf *<span style="color: rgba(0, 0, 0, 1)">gobuf, fn, ctxt unsafe.Pointer) {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> newg的栈顶,目前newg栈上只有fn函数的参数,sp指向的是fn的第一个参数</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    sp :=<span style="color: rgba(0, 0, 0, 1)"> buf.sp
</span><span style="color: rgba(0, 128, 128, 1)">18</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为返回地址预留空间</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>    sp -=<span style="color: rgba(0, 0, 0, 1)"> sys.PtrSize
</span><span style="color: rgba(0, 128, 128, 1)">20</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> buf.pc中设置的是goexit函数中的第二条指令</span>
<span style="color: rgba(0, 128, 128, 1)">21</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 因为栈是自顶向下,先进后出,所以这里伪装fn是被goexit函数调用的,goexit在前fn在后</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使得fn返回后到goexit继续执行,以完成一些清理工作。</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>    *(*uintptr)(unsafe.Pointer(sp)) =<span style="color: rgba(0, 0, 0, 1)"> buf.pc
</span><span style="color: rgba(0, 128, 128, 1)">24</span>    buf.sp = sp <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 重新设置栈顶</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将pc指向goroutine的任务函数fn,这样当goroutine获得执行权时,从任务函数入口开始执行</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果是主协程,那么fn就是runtime.main,从这里开始执行</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>    buf.pc =<span style="color: rgba(0, 0, 0, 1)"> uintptr(fn)
</span><span style="color: rgba(0, 128, 128, 1)">28</span>    buf.ctxt =<span style="color: rgba(0, 0, 0, 1)"> ctxt
</span><span style="color: rgba(0, 128, 128, 1)">29</span> }</pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">可以看到在newg初始化时进行的一系列设置工作,将goexit先压入栈顶,然后伪造sp位置,让cpu看起来是从goexit中调用的协程任务函数,然后将pc指针指向任务函数,当协程被执行时,从pc处开始执行,任务函数执行完毕后执行goexit;</span></div>
<div style="line-height: 1.75">&nbsp;</div>
<div style="line-height: 1.75"><span style="font-size: inherit">这里是设置工作,具体的切换工作,需要经由schedule调度函数选中一个g,进入execute函数设置g的相关状态和栈保护字段等信息,然后进入gogo函数,通过汇编语言,将CPU寄存器以及函数调用栈切换为g的sched中相关指针和协程栈。gogo函数源码如下:</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gogo的具体汇编代码位于asm_amd64.s中</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> func gogo(buf *gobuf)</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> restore state from Gobuf; longjmp</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> TEXT runtime·gogo(SB), NOSPLIT, $0-8
<span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 0(FP)表示第一个参数,即buf=&amp;gp.sched</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    MOVQ   buf+0(FP), BX   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gobuf</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DX = gp.sched.g,DX代表数据寄存器</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   gobuf_g(BX), DX
</span><span style="color: rgba(0, 128, 128, 1)">10</span>    MOVQ   0(DX), CX   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> make sure g != nil</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    JMP    gogo&lt;&gt;<span style="color: rgba(0, 0, 0, 1)">(SB)
</span><span style="color: rgba(0, 128, 128, 1)">12</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> TEXT gogo&lt;&gt;(SB), NOSPLIT, $0
<span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将tls保存到CX寄存器</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">   get_tls(CX)
</span><span style="color: rgba(0, 128, 128, 1)">16</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 下面这条指令把当前要运行的g(上面第9行中已经把go_buf中的g放入到了DX中),</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 放入CX寄存器的g位置即tls这个位置,也就是线程的本地存储中,</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这样下次runtime中调用getg时获取的就是这个g</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   DX, g(CX)
</span><span style="color: rgba(0, 128, 128, 1)">20</span>    MOVQ   DX, R14       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> set the g register</span>
<span style="color: rgba(0, 128, 128, 1)">21</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 把CPU的SP寄存器设置为g.sched.sp这样就完成了栈的切换,从g0切换为g</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>    MOVQ   gobuf_sp(BX), SP   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> restore SP</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将ret、ctxt、bp分别存入对应的寄存器,完成了CPU上下文的切换</span>
<span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   gobuf_ret(BX), AX
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   gobuf_ctxt(BX), DX
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   gobuf_bp(BX), BP
</span><span style="color: rgba(0, 128, 128, 1)">27</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 清空sched的值,相关值已经存入到寄存器中,这里清空后可以减少GC的工作量</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>    MOVQ   $0, gobuf_sp(BX)   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> clear to help garbage collector</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>    MOVQ   $0<span style="color: rgba(0, 0, 0, 1)">, gobuf_ret(BX)
</span><span style="color: rgba(0, 128, 128, 1)">30</span>    MOVQ   $0<span style="color: rgba(0, 0, 0, 1)">, gobuf_ctxt(BX)
</span><span style="color: rgba(0, 128, 128, 1)">31</span>    MOVQ   $0<span style="color: rgba(0, 0, 0, 1)">, gobuf_bp(BX)
</span><span style="color: rgba(0, 128, 128, 1)">32</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 把sched.pc放入BX寄存器</span>
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   gobuf_pc(BX), BX
</span><span style="color: rgba(0, 128, 128, 1)">34</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> JMP把BX的值放入CPU的IP寄存器,所以这时候CPU从该地址开始继续执行指令</span>
<span style="color: rgba(0, 128, 128, 1)">35</span>    JMP    BX</pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">AX、BX、CX、DX是8086处理器的4个数据寄存器,可以简单认为相当于4个硬件的变量;</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">上文总体来说,将g存入到tls中(线程的本地存储),设置SP和相关寄存器为g.sched中的字段(SP、ret、ctxt、bp),然后跳转到pc指针位置执行指令</span></div>
<h4 id="1mm0-1654344882850" style="line-height: 1.75"><span style="font-weight: bold; font-size: 18px">3.1.6 G的退出处理</span></h4>
<div style="line-height: 1.75"><span style="font-size: inherit">协程栈的退出需要分为两种情况,即运行main函数的主协程和普通的用户协程;</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit; font-weight: bold">主协程</span><span style="font-size: inherit">的fn任务函数位于proc.go中的main函数中,对于主协程g.shched.pc指向的也是这个位置,这里会调用用户的mian函数(main_main),main_main运行完毕后,会调用exit(0)直接退出,而不会跑到goexit函数中。</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime/proc.go 中的main函数</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The main goroutine.</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">func main() {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    g :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">.................
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    fn := main_main <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> make an indirect call, as the linker doesn't know the address of the main package when laying down the runtime</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">   fn()
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">..................
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">..................
</span><span style="color: rgba(0, 128, 128, 1)">11</span>    exit(0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">..................
</span><span style="color: rgba(0, 128, 128, 1)">13</span> }</pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">用户协程因为将goexit作为协程栈栈底,所以当执行完协程任务函数时,会执行goexit函数,goexit是一段汇编指令:</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The top-most function running on a goroutine</span>
<span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> returns to goexit+PCQuantum.</span>
<span style="color: rgba(0, 128, 128, 1)">3</span> TEXT runtime·goexit(SB),NOSPLIT|TOPFRAME,$0-0
<span style="color: rgba(0, 128, 128, 1)">4</span>    BYTE   $0x90<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> NOP</span>
<span style="color: rgba(0, 128, 128, 1)">5</span>    CALL   runtime·goexit1(SB)    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> does not return</span>
<span style="color: rgba(0, 128, 128, 1)">6</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> traceback from goexit1 must hit code range of goexit</span>
<span style="color: rgba(0, 128, 128, 1)">7</span>    BYTE   $0x90<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> NOP</span></pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">这里直接调用goexit1,goexit1位于runtime/proc.go中</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Finishes execution of the current goroutine.</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">func goexit1() {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> raceenabled {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">      racegoend()
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> trace.enabled {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">      traceGoEnd()
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   mcall(goexit0)
</span><span style="color: rgba(0, 128, 128, 1)">10</span> }</pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">通过mcall调用goexit0,mcall是一段汇编代码它的作用是把执行的栈切换到g0的栈</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> TEXT runtime·mcall&lt;ABIInternal&gt;(SB), NOSPLIT, $0-8
<span style="color: rgba(0, 128, 128, 1)"> 2</span>    MOVQ   AX, DX <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> DX = fn</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> save state in g-&gt;sched</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> mcall返回地址放入BX中</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    MOVQ   0(SP), BX<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> caller's PC</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 下面部分是保存g的执行上下文,pc、sp、bp</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> g.shced.pc = BX</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    MOVQ   BX, (g_sched+<span style="color: rgba(0, 0, 0, 1)">gobuf_pc)(R14)
</span><span style="color: rgba(0, 128, 128, 1)">10</span>    LEAQ   fn+0(FP), BX   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> caller's SP</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    MOVQ   BX, (g_sched+<span style="color: rgba(0, 0, 0, 1)">gobuf_sp)(R14)
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    MOVQ   BP, (g_sched+<span style="color: rgba(0, 0, 0, 1)">gobuf_bp)(R14)
</span><span style="color: rgba(0, 128, 128, 1)">13</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> switch to m-&gt;g0 &amp; its stack, call fn</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将g.m保存到BX寄存器中</span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   g_m(R14), BX
</span><span style="color: rgba(0, 128, 128, 1)">17</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这段代码意思是从m结构体中获取g0字段保存到SI中</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>    MOVQ   m_g0(BX), SI   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> SI = g.m.g0</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>    CMPQ   SI, R14    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> if g == m-&gt;g0 call badmcall</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goodm中完成了从g的栈切换到g0的栈</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">   JNE    goodm
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">   JMP    runtime·badmcall(SB)
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">goodm:
</span><span style="color: rgba(0, 128, 128, 1)">24</span>    MOVQ   R14, AX       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> AX (and arg 0) = g</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    MOVQ   SI, R14       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> g = g.m.g0</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>    get_tls(CX)       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Set G in TLS</span>
<span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">   MOVQ   R14, g(CX)
</span><span style="color: rgba(0, 128, 128, 1)">28</span>    MOVQ   (g_sched+gobuf_sp)(R14), SP    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> sp = g0.sched.sp</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>    PUSHQAX <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> open up space for fn's arg spill slot</span>
<span style="color: rgba(0, 128, 128, 1)">30</span>    MOVQ   0<span style="color: rgba(0, 0, 0, 1)">(DX), R12
</span><span style="color: rgba(0, 128, 128, 1)">31</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里意思是调用goexit0(g)</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>    CALL   R12       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> fn(g)</span>
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">   POPQ   AX
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">   JMP    runtime·badmcall2(SB)
</span><span style="color: rgba(0, 128, 128, 1)">35</span>    RET</pre>
</div>
</div>
<div style="line-height: 1.75"><span style="font-size: inherit">goexit0代码位于runtime/proc.go中,他主要完成最后的清理工作:</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">1、把g的状态从——Gruning变为Gdead</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">2、清空g的一些字段</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">3、接触g与m的绑定关系,即g.m = nil;m.currg = nil</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">4、把g放入p的freeg队列中,下次创建g可以直接获取,而不用从内存分配</span></div>
<div style="line-height: 1.75"><span style="font-size: inherit">5、调用schedule进入下一次调度循环</span></div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这段代码执行在g0的栈上,gp是我们要处理退出的g的结构体指针</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goexit continuation on g0.</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> func goexit0(gp *<span style="color: rgba(0, 0, 0, 1)">g) {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    _g_ := getg() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取g0</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更改g的状态为_Gdead</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">   casgstatus(gp, _Grunning, _Gdead)
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> isSystemGoroutine(gp, <span style="color: rgba(0, 0, 255, 1)">false</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       atomic.Xadd(&amp;sched.ngsys, -1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">10</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 清空g的一些字段</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    gp.m =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    locked := gp.lockedm != 0
<span style="color: rgba(0, 128, 128, 1)">13</span>    gp.lockedm = 0
<span style="color: rgba(0, 128, 128, 1)">14</span>    _g_.m.lockedg = 0
<span style="color: rgba(0, 128, 128, 1)">15</span>    gp.preemptStop = <span style="color: rgba(0, 0, 255, 1)">false</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>    gp.paniconfault = <span style="color: rgba(0, 0, 255, 1)">false</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    gp._defer = nil <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> should be true already but just in case.</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>    gp._panic = nil <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> non-nil for Goexit during panic. points at stack-allocated data.</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>    gp.writebuf =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">20</span>    gp.waitreason = 0
<span style="color: rgba(0, 128, 128, 1)">21</span>    gp.param =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">22</span>    gp.labels =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">23</span>    gp.timer =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">24</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> gcBlackenEnabled != 0 &amp;&amp; gp.gcAssistBytes &gt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Flush assist credit to the global pool. This gives</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> better information to pacing if the application is</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> rapidly creating an exiting goroutines.</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>       assistWorkPerByte := float64frombits(atomic.Load64(&amp;<span style="color: rgba(0, 0, 0, 1)">gcController.assistWorkPerByte))
</span><span style="color: rgba(0, 128, 128, 1)">30</span>       scanCredit := int64(assistWorkPerByte *<span style="color: rgba(0, 0, 0, 1)"> float64(gp.gcAssistBytes))
</span><span style="color: rgba(0, 128, 128, 1)">31</span>       atomic.Xaddint64(&amp;<span style="color: rgba(0, 0, 0, 1)">gcController.bgScanCredit, scanCredit)
</span><span style="color: rgba(0, 128, 128, 1)">32</span>       gp.gcAssistBytes = 0
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">34</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接触g与m的绑定关系</span>
<span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)">   dropg()
</span><span style="color: rgba(0, 128, 128, 1)">36</span>
<span style="color: rgba(0, 128, 128, 1)">37</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> GOARCH == "wasm" { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> no threads yet on wasm</span>
<span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">      gfput(_g_.m.p.ptr(), gp)
</span><span style="color: rgba(0, 128, 128, 1)">39</span>       schedule() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> never returns</span>
<span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">41</span>
<span style="color: rgba(0, 128, 128, 1)">42</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m.lockedInt != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">43</span>       print("invalid m-&gt;lockedInt = ", _g_.m.lockedInt, "\n"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">44</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("internal lockOSThread error"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">46</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将g加入p的空闲队列</span>
<span style="color: rgba(0, 128, 128, 1)">47</span> <span style="color: rgba(0, 0, 0, 1)">   gfput(_g_.m.p.ptr(), gp)
</span><span style="color: rgba(0, 128, 128, 1)">48</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> locked {
</span><span style="color: rgba(0, 128, 128, 1)">49</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The goroutine may have locked this thread because</span>
<span style="color: rgba(0, 128, 128, 1)">50</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> it put it in an unusual kernel state. Kill it</span>
<span style="color: rgba(0, 128, 128, 1)">51</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> rather than returning it to the thread pool.</span>
<span style="color: rgba(0, 128, 128, 1)">52</span>
<span style="color: rgba(0, 128, 128, 1)">53</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Return to mstart, which will release the P and exit</span>
<span style="color: rgba(0, 128, 128, 1)">54</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the thread.</span>
<span style="color: rgba(0, 128, 128, 1)">55</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> GOOS != "plan9" { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> See golang.org/issue/22227.</span>
<span style="color: rgba(0, 128, 128, 1)">56</span>          gogo(&amp;<span style="color: rgba(0, 0, 0, 1)">_g_.m.g0.sched)
</span><span style="color: rgba(0, 128, 128, 1)">57</span>       } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">58</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Clear lockedExt on plan9 since we may end up re-using</span>
<span style="color: rgba(0, 128, 128, 1)">59</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> this thread.</span>
<span style="color: rgba(0, 128, 128, 1)">60</span>          _g_.m.lockedExt = 0
<span style="color: rgba(0, 128, 128, 1)">61</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">62</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">63</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行下一轮调度</span>
<span style="color: rgba(0, 128, 128, 1)">64</span> <span style="color: rgba(0, 0, 0, 1)">   schedule()
</span><span style="color: rgba(0, 128, 128, 1)">65</span> }</pre>
</div>
<p>&nbsp;</p>
<h3 id="RByR-1654345334948">3.2 P源码部分</h3>
<h4 id="TyPn-1654345334952">3.2.1 P的结构</h4>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime/runtime2.go</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">type p struct {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 全局变量allp中的索引位置</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">   id          int32
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> p的状态标识</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    status      uint32 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> one of pidle/prunning/...</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">   link      puintptr
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调用schedule的次数,每次调用schedule这个值会加1</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>    schedtick   uint32   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> incremented on every scheduler call</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 系统调用的次数,每次进行系统调用加1</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>    syscalltick uint32   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> incremented on every system call</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用于sysmon协程记录被监控的p的系统调用时间和运行时间</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>    sysmonticksysmontick <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> last tick observed by sysmon</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指向绑定的m,p如果是idle状态这个值为nil</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>    m         muintptr   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> back-link to associated m (nil if idle)</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用于分配微小对象和小对象的一个块的缓存空间,里面有各种不同等级的span</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>    mcache      *<span style="color: rgba(0, 0, 0, 1)">mcache
</span><span style="color: rgba(0, 128, 128, 1)">19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 一个chunk大小(512kb)的内存空间,用来对堆上内存分配的缓存优化达到无锁访问的目的</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">   pcache      pageCache
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">   raceprocctx uintptr
</span><span style="color: rgba(0, 128, 128, 1)">22</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>    deferpool    []*_defer <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pool of available defer structs of different sizes (see panic.go)</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>    deferpoolbuf *<span style="color: rgba(0, 0, 0, 1)">_defer
</span><span style="color: rgba(0, 128, 128, 1)">25</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen.</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 可以分配给g的id的缓存,每次会一次性申请16个</span>
<span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">   goidcache    uint64
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">   goidcacheend uint64
</span><span style="color: rgba(0, 128, 128, 1)">30</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Queue of runnable goroutines. Accessed without lock.</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 本地可运行的G队列的头部和尾部,达到无锁访问</span>
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">   runqhead uint32
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">   runqtail uint32
</span><span style="color: rgba(0, 128, 128, 1)">35</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 本地可运行的g队列,是一个使用数组实现的循环队列</span>
<span style="color: rgba(0, 128, 128, 1)">36</span>    runq   guintptr
</span><span style="color: rgba(0, 128, 128, 1)">37</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 下一个待运行的g,这个g的优先级最高</span>
<span style="color: rgba(0, 128, 128, 1)">38</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果当前g运行完后还有剩余可用时间,那么就应该运行这个runnext的g</span>
<span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)">   runnext guintptr
</span><span style="color: rgba(0, 128, 128, 1)">40</span>
<span style="color: rgba(0, 128, 128, 1)">41</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Available G's (status == Gdead)</span>
<span style="color: rgba(0, 128, 128, 1)">42</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> p上的空闲队列列表</span>
<span style="color: rgba(0, 128, 128, 1)">43</span> <span style="color: rgba(0, 0, 0, 1)">   gFree struct {
</span><span style="color: rgba(0, 128, 128, 1)">44</span> <span style="color: rgba(0, 0, 0, 1)">      gList
</span><span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">      n int32
</span><span style="color: rgba(0, 128, 128, 1)">46</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">47</span>
<span style="color: rgba(0, 128, 128, 1)">48</span> <span style="color: rgba(0, 0, 0, 1)">   ............
</span><span style="color: rgba(0, 128, 128, 1)">49</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用于内存对齐</span>
<span style="color: rgba(0, 128, 128, 1)">50</span>    _ uint32 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Alignment for atomic fields below</span>
<span style="color: rgba(0, 128, 128, 1)">51</span> <span style="color: rgba(0, 0, 0, 1)">.......................
</span><span style="color: rgba(0, 128, 128, 1)">52</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 是否被抢占</span>
<span style="color: rgba(0, 128, 128, 1)">53</span> <span style="color: rgba(0, 0, 0, 1)">   preempt bool
</span><span style="color: rgba(0, 128, 128, 1)">54</span>
<span style="color: rgba(0, 128, 128, 1)">55</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Padding is no longer needed. False sharing is now not a worry because p is large enough</span>
<span style="color: rgba(0, 128, 128, 1)">56</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> that its size class is an integer multiple of the cache line size (for any of our architectures).</span>
<span style="color: rgba(0, 128, 128, 1)">57</span> }</pre>
</div>
</div>
<div>通过这里的结构可以看出,虽然P叫做逻辑处理器Processor,实际上它更多是资源的管理者,其中包含了可运行的g队列资源、内存分配的资源、以及对调度循环、系统调用、sysmon协程的相关记录。通过P的资源管理来尽量实现无锁访问,提升应用性能。</div>
<div>&nbsp;</div>
<h4 id="BGXP-1654345335074">3.2.2 P的状态</h4>
<div>当程序刚开始运行进行初始化时,所有的P都处于_Pgcstop状态,随着的P的初始化(runtime.procresize),会被设置为_Pidle状态。</div>
<div>当M需要运行时会调用runtime.acquirep来使P变为_Prunning状态,并通过runtime.releasep来释放,重新变为_Pidele。</div>
<div>当G执行时需要进入系统调用,P会被设置为_Psyscall,如果这个时候被系统监控抢夺(runtime.retake),则P会被重新修改为_Pidle。</div>
<div>如果在程序中发生GC,则P会被设置为_Pgcstop,并在runtime.startTheWorld时重新调整为_Prunning。</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604202640691-468374857.png" alt="0" data-media-type="image"></div>
<div>(这部分文字来自《Go程序员面试宝典》,图片来自这里)</div>
<div>&nbsp;
<h4 id="bYjc-1654345430951">3.2.3 P的创建</h4>
<div>P的初始化是在schedinit函数中调用的,schedinit函数是在runtime的汇编启动代码里调用的。</div>
</div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> <span style="color: rgba(0, 0, 0, 1)">...........................
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">CALL    runtime·args(SB)
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 0, 0, 1)">CALL   runtime·osinit(SB)
</span><span style="color: rgba(0, 128, 128, 1)">4</span> <span style="color: rgba(0, 0, 0, 1)">CALL   runtime·schedinit(SB)
</span><span style="color: rgba(0, 128, 128, 1)">5</span> ...........................</pre>
</div>
<div>shcedinit中通过调用procresize进行P的分配。P的个数默认等于CPU核数,如果设置了GOMAXPROCS环境变量,则会采用设置的值来确定P的个数。所以runtime.GOMAXPROCS是限制的并行线程数量,而不是系统线程即M的总数,M是按需创建。</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func schedinit() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">.................
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    sched.lastpoll =<span style="color: rgba(0, 0, 0, 1)"> uint64(nanotime())
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    procs :=<span style="color: rgba(0, 0, 0, 1)"> ncpu
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> n, ok := atoi32(gogetenv("GOMAXPROCS")); ok &amp;&amp; n &gt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       procs =<span style="color: rgba(0, 0, 0, 1)"> n
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> procresize(procs) !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("unknown runnable goroutine during bootstrap"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">13</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> World is effectively started now, as P's can run.</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">   worldStarted()
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">   .....................
</span><span style="color: rgba(0, 128, 128, 1)">17</span> }</pre>
</div>
<div>上面获取ncpu的个数,然后传递给procresize函数。</div>
<div>无论是初始化时的分配,还是后期调整,都是通过procresize来创建p以及初始化</div>
<div>
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func procresize(nprocs int32) *<span style="color: rgba(0, 0, 0, 1)">p {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">.............................
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    old :=<span style="color: rgba(0, 0, 0, 1)"> gomaxprocs
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">......................
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> nprocs &gt;<span style="color: rgba(0, 0, 0, 1)"> int32(len(allp)) {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Synchronize with retake, which could be running</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> concurrently since it doesn't run on a P.</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>       lock(&amp;<span style="color: rgba(0, 0, 0, 1)">allpLock)
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> nprocs &lt;=<span style="color: rgba(0, 0, 0, 1)"> int32(cap(allp)) {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果需要的p小于allp这个全局变量(切片)的cap能力,取其中的一部分</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>          allp =<span style="color: rgba(0, 0, 0, 1)"> allp[:nprocs]
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">13</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 否则创建nprocs数量的p,并把allp的中复制给nallp</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>          nallp := make([]*<span style="color: rgba(0, 0, 0, 1)">p, nprocs)
</span><span style="color: rgba(0, 128, 128, 1)">15</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Copy everything up to allp's cap so we</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> never lose old allocated Ps.</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">         copy(nallp, allp[:cap(allp)])
</span><span style="color: rgba(0, 128, 128, 1)">18</span>          allp =<span style="color: rgba(0, 0, 0, 1)"> nallp
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">....................................
</span><span style="color: rgba(0, 128, 128, 1)">21</span>       unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">allpLock)
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">23</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 进行p的初始化</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    <span style="color: rgba(0, 0, 255, 1)">for</span> i := old; i &lt; nprocs; i++<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>       pp :=<span style="color: rgba(0, 0, 0, 1)"> allp
</span><span style="color: rgba(0, 128, 128, 1)">27</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> pp ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">28</span>          pp = <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">(p)
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">      pp.init(i)
</span><span style="color: rgba(0, 128, 128, 1)">31</span>       atomicstorep(unsafe.Pointer(&amp;<span style="color: rgba(0, 0, 0, 1)">allp), unsafe.Pointer(pp))
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">...............................
</span><span style="color: rgba(0, 128, 128, 1)">34</span>    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> runnablePs
</span><span style="color: rgba(0, 128, 128, 1)">35</span> }</pre>
</div>
<div>在启动时候会根据CPU核数和runtime.GOMAXPROCS来设置p的个数,并由一个叫做allp的切片来为主,后期可以通过设置GOMAXPROCS来调整P的个数,但是性能消耗很大,会进行STW;</div>
<div>
<h3 id="5LkL-1654345498061">3.3 M源码部分</h3>
<h4 id="5eg9-1654345498065">3.3.1 M的结构</h4>
<div>M即Machine,代表一个进程中的工作线程,结构体m保存了M自身使用的栈信息、当前正在M上执行的G,以及绑定M的P信息等。</div>
<div>我们来看下m的结构体:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">type m struct {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 每个m都有一个对应的g0线程,用来执行调度代码,</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 当需要执行用户代码的时候,g0会与用户goroutine发生协程栈切换</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    g0      *g   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goroutine with scheduling stack</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    morebuf gobuf<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gobuf arg to morestack</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">........................
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> tls作为线程的本地存储</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 其中可以在任意时刻获取绑定到当前线程上的协程g、结构体m、逻辑处理器p、特殊协程g0等信息</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    tls         uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> thread-local storage (for x86 extern register)</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">   mstartfn      func()
</span><span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 指向正在运行的goroutine对象</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>    curg          *g       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> current running goroutine</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>    caughtsig   guintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> goroutine running during fatal signal</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 与当前工作线程绑定的p</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    p             puintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> attached p for executing go code (nil if not executing go code)</span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">   nextp         puintptr
</span><span style="color: rgba(0, 128, 128, 1)">17</span>    oldp          puintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the p that was attached before executing a syscall</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   id            int64
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">   mallocing   int32
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">   throwing      int32
</span><span style="color: rgba(0, 128, 128, 1)">21</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 与禁止抢占相关的字段,如果该字段不等于空字符串,要保持curg一直在这个m上运行</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>    preemptoff    string <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> if != "", keep curg running on this m</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> locks也是判断g能否被抢占的一个标识</span>
<span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">   locks         int32
</span><span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">   dying         int32
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">   profilehz   int32
</span><span style="color: rgba(0, 128, 128, 1)">27</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> spining为true标识当前m正在处于自己找工作的自旋状态,</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 首先检查全局队列看是否有工作,然后检查network poller,尝试执行GC任务</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">或者偷一部分工作,如果都没有则会进入休眠状态</span>
<span style="color: rgba(0, 128, 128, 1)">30</span>    spinning      bool <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> m is out of work and is actively looking for work</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表示m正阻塞在note上</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>    blocked       bool <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> m is blocked on a note</span>
<span style="color: rgba(0, 128, 128, 1)">33</span> <span style="color: rgba(0, 0, 0, 1)">.........................
</span><span style="color: rgba(0, 128, 128, 1)">34</span>    doesPark      bool      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> non-P running threads: sysmon and newmHandoff never use .park</span>
<span style="color: rgba(0, 128, 128, 1)">35</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 没有goroutine需要运行时,工作线程睡眠在这个park成员上</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">   park          note
</span><span style="color: rgba(0, 128, 128, 1)">37</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 记录所有工作线程的一个链表</span>
<span style="color: rgba(0, 128, 128, 1)">38</span>    alllink       *m <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> on allm</span>
<span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)">   schedlink   muintptr
</span><span style="color: rgba(0, 128, 128, 1)">40</span> <span style="color: rgba(0, 0, 0, 1)">   lockedg       guintptr
</span><span style="color: rgba(0, 128, 128, 1)">41</span>    createstack   uintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> stack that created this thread.</span>
<span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">.............................
</span><span style="color: rgba(0, 128, 128, 1)">43</span> }</pre>
</div>
</div>
<h4 id="TLWV-1654345498163">3.3.2 M的状态</h4>
<div>M的状态并没有向P和G那样有多个状态常量,它只有自旋和非自旋两种状态</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">mstart
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   |
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">    v      
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> +------+       找不到可执行任务         +-------+
<span style="color: rgba(0, 128, 128, 1)"> 5</span> |unspin| ----------------------------&gt; |spining|
<span style="color: rgba(0, 128, 128, 1)"> 6</span> |      | &lt;---------------------------- |       |
<span style="color: rgba(0, 128, 128, 1)"> 7</span> +------+       找到可执行任务            +-------+
<span style="color: rgba(0, 128, 128, 1)"> 8</span>   ^                                    |<span style="color: rgba(0, 0, 0, 1)"> stopm
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>   |<span style="color: rgba(0, 0, 0, 1)">               wakep                  v
</span><span style="color: rgba(0, 128, 128, 1)">10</span> notewakeup &lt;-------------------------notesleep </pre>
</div>
</div>
<div>当M没有工作时,它会自旋的来找工作,首先检查全局队列看是否有工作,然后检查network poller,尝试执行GC任务,或者偷一部分工作,如果都没有则会进入休眠状态。当被其他工作线程唤醒,又会进入自旋状态。</div>
<h4 id="UwPn-1654345498192">3.3.3 M的创建</h4>
<div>runtime/proc.go中的newm函数用来新建m,而最终是根据不同的系统,通过系统调用来创建线程。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> newm创建一个新的m,将从fn或者调度程序开始执行</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> func newm(fn func(), _p_ *<span style="color: rgba(0, 0, 0, 1)">p, id int64) {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里实现m的创建</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    mp :=<span style="color: rgba(0, 0, 0, 1)"> allocm(_p_, fn, id)
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    mp.doesPark = (_p_ !=<span style="color: rgba(0, 0, 0, 1)"> nil)
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">   mp.nextp.set(_p_)
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    mp.sigmask =<span style="color: rgba(0, 0, 0, 1)"> initSigmask
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> gp := getg(); gp != nil &amp;&amp; gp.m != nil &amp;&amp; (gp.m.lockedExt != 0 || gp.m.incgo) &amp;&amp; GOOS != "plan9"<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 我们处于锁定的M或可能由C启动的线程。此线程的内核状态可能很奇怪(用户可能已将其锁定为此目的)。</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 我们不想将其克隆到另一个线程中。相反,请求一个已知良好的线程为我们创建线程。</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>       lock(&amp;<span style="color: rgba(0, 0, 0, 1)">newmHandoff.lock)
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> newmHandoff.haveTemplateThread == 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">13</span>          <span style="color: rgba(0, 0, 255, 1)">throw</span>("on a locked thread with no template thread"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">15</span>       mp.schedlink =<span style="color: rgba(0, 0, 0, 1)"> newmHandoff.newm
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">      newmHandoff.newm.set(mp)
</span><span style="color: rgba(0, 128, 128, 1)">17</span>       <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> newmHandoff.waiting {
</span><span style="color: rgba(0, 128, 128, 1)">18</span>          newmHandoff.waiting = <span style="color: rgba(0, 0, 255, 1)">false</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>          notewakeup(&amp;<span style="color: rgba(0, 0, 0, 1)">newmHandoff.wake)
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">21</span>       unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">newmHandoff.lock)
</span><span style="color: rgba(0, 128, 128, 1)">22</span>       <span style="color: rgba(0, 0, 255, 1)">return</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">24</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里分配真正的的操作系统线程</span>
<span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">   newm1(mp)
</span><span style="color: rgba(0, 128, 128, 1)">26</span> }</pre>
</div>
</div>
<div>allocm函数中实现m的创建,以及对应的g0协程的设置</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func allocm(_p_ *p, fn func(), id int64) *<span style="color: rgba(0, 0, 0, 1)">m {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取当前运行的g</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将_g_对应的m的locks加1,防止被抢占</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    acquirem() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> disable GC because it can be called from sysmon</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m.p == 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       acquirep(_p_) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> temporarily borrow p for mallocs in this function</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 检查是有有空闲的m可以释放,主要目的是释放m上的g0占用的系统栈</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> sched.freem !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       <span style="color: rgba(0, 0, 255, 1)">var</span> newList *<span style="color: rgba(0, 0, 0, 1)">m
</span><span style="color: rgba(0, 128, 128, 1)">14</span>       <span style="color: rgba(0, 0, 255, 1)">for</span> freem := sched.freem; freem !=<span style="color: rgba(0, 0, 0, 1)"> nil; {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> freem.freeWait != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>             next :=<span style="color: rgba(0, 0, 0, 1)"> freem.freelink
</span><span style="color: rgba(0, 128, 128, 1)">17</span>             freem.freelink =<span style="color: rgba(0, 0, 0, 1)"> newList
</span><span style="color: rgba(0, 128, 128, 1)">18</span>             newList =<span style="color: rgba(0, 0, 0, 1)"> freem
</span><span style="color: rgba(0, 128, 128, 1)">19</span>             freem =<span style="color: rgba(0, 0, 0, 1)"> next
</span><span style="color: rgba(0, 128, 128, 1)">20</span>             <span style="color: rgba(0, 0, 255, 1)">continue</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">22</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> stackfree must be on the system stack, but allocm is</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> reachable off the system stack transitively from</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> startm.</span>
<span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 0, 0, 1)">         systemstack(func() {
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">            stackfree(freem.g0.stack)
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">         })
</span><span style="color: rgba(0, 128, 128, 1)">28</span>          freem =<span style="color: rgba(0, 0, 0, 1)"> freem.freelink
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">30</span>       sched.freem =<span style="color: rgba(0, 0, 0, 1)"> newList
</span><span style="color: rgba(0, 128, 128, 1)">31</span>       unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">33</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一个m结构体</span>
<span style="color: rgba(0, 128, 128, 1)">34</span>    mp := <span style="color: rgba(0, 0, 255, 1)">new</span><span style="color: rgba(0, 0, 0, 1)">(m)
</span><span style="color: rgba(0, 128, 128, 1)">35</span>    mp.mstartfn = fn <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将fn设置为m启动后执行的函数</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">   mcommoninit(mp, id)
</span><span style="color: rgba(0, 128, 128, 1)">37</span>
<span style="color: rgba(0, 128, 128, 1)">38</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> In case of cgo or Solaris or illumos or Darwin, pthread_create will make us a stack.</span>
<span style="color: rgba(0, 128, 128, 1)">39</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Windows and Plan 9 will layout sched stack on OS stack.</span>
<span style="color: rgba(0, 128, 128, 1)">40</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> iscgo ||<span style="color: rgba(0, 0, 0, 1)"> mStackIsSystemAllocated() {
</span><span style="color: rgba(0, 128, 128, 1)">41</span>       mp.g0 = malg(-1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">42</span>    } <span style="color: rgba(0, 0, 255, 1)">else</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">43</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置m对应的g0,并设置对应大小的g0协程栈,g0是8kb</span>
<span style="color: rgba(0, 128, 128, 1)">44</span>       mp.g0 = malg(8192 *<span style="color: rgba(0, 0, 0, 1)"> sys.StackGuardMultiplier)
</span><span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">46</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置g0对应的m</span>
<span style="color: rgba(0, 128, 128, 1)">47</span>    mp.g0.m =<span style="color: rgba(0, 0, 0, 1)"> mp
</span><span style="color: rgba(0, 128, 128, 1)">48</span>
<span style="color: rgba(0, 128, 128, 1)">49</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _p_ ==<span style="color: rgba(0, 0, 0, 1)"> _g_.m.p.ptr() {
</span><span style="color: rgba(0, 128, 128, 1)">50</span> <span style="color: rgba(0, 0, 0, 1)">      releasep()
</span><span style="color: rgba(0, 128, 128, 1)">51</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">52</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 解除_g_的m的禁止抢占状态。</span>
<span style="color: rgba(0, 128, 128, 1)">53</span> <span style="color: rgba(0, 0, 0, 1)">   releasem(_g_.m)
</span><span style="color: rgba(0, 128, 128, 1)">54</span>
<span style="color: rgba(0, 128, 128, 1)">55</span>    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> mp
</span><span style="color: rgba(0, 128, 128, 1)">56</span> }</pre>
</div>
</div>
<div>为什么m.locks加1可以禁止抢占,防止GC;因为判断是否可以抢占,其中一个因素就是要m.locks=0</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> func canPreemptM(mp *<span style="color: rgba(0, 0, 0, 1)">m) bool {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>    <span style="color: rgba(0, 0, 255, 1)">return</span> mp.locks == 0 &amp;&amp; mp.mallocing == 0 &amp;&amp; mp.preemptoff == "" &amp;&amp; mp.p.ptr().status ==<span style="color: rgba(0, 0, 0, 1)"> _Prunning
</span><span style="color: rgba(0, 128, 128, 1)">3</span> }</pre>
</div>
</div>
<div>allocm函数实现了m的创建,但是这只是runtime层面的一个数据结构,还并没有在系统中有真正的线程。再来看newm1函数:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> func newm1(mp *<span style="color: rgba(0, 0, 0, 1)">m) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span> <span style="color: rgba(0, 0, 0, 1)">..............
</span><span style="color: rgba(0, 128, 128, 1)">3</span>    execLock.rlock() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Prevent process clone.</span>
<span style="color: rgba(0, 128, 128, 1)">4</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一个系统线程,并且传入该 mp 绑定的 g0 的栈顶指针</span>
<span style="color: rgba(0, 128, 128, 1)">5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 让系统线程执行 mstart 函数,后面的逻辑都在 mstart 函数中</span>
<span style="color: rgba(0, 128, 128, 1)">6</span> <span style="color: rgba(0, 0, 0, 1)">   newosproc(mp)
</span><span style="color: rgba(0, 128, 128, 1)">7</span> <span style="color: rgba(0, 0, 0, 1)">   execLock.runlock()
</span><span style="color: rgba(0, 128, 128, 1)">8</span> }</pre>
</div>
</div>
<div>实际是通过newostproc来创建系统线程;</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604202640694-180660561.png" alt="0" data-media-type="image"></div>
<div>可以看到这个函数在不同的系统中有不同的实现,我们主要看linux系统,即os_linux.go文件代码:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span> func newosproc(mp *<span style="color: rgba(0, 0, 0, 1)">m) {
</span><span style="color: rgba(0, 128, 128, 1)">2</span>    stk :=<span style="color: rgba(0, 0, 0, 1)"> unsafe.Pointer(mp.g0.stack.hi)
</span><span style="color: rgba(0, 128, 128, 1)">3</span> <span style="color: rgba(0, 0, 0, 1)">.........................
</span><span style="color: rgba(0, 128, 128, 1)">4</span>    <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> oset sigset
</span><span style="color: rgba(0, 128, 128, 1)">5</span>    sigprocmask(_SIG_SETMASK, &amp;sigset_all, &amp;<span style="color: rgba(0, 0, 0, 1)">oset)
</span><span style="color: rgba(0, 128, 128, 1)">6</span>    ret :=<span style="color: rgba(0, 0, 0, 1)"> clone(cloneFlags, stk, unsafe.Pointer(mp), unsafe.Pointer(mp.g0), unsafe.Pointer(funcPC(mstart)))
</span><span style="color: rgba(0, 128, 128, 1)">7</span>    sigprocmask(_SIG_SETMASK, &amp;<span style="color: rgba(0, 0, 0, 1)">oset, nil)
</span><span style="color: rgba(0, 128, 128, 1)">8</span> <span style="color: rgba(0, 0, 0, 1)">.........................
</span><span style="color: rgba(0, 128, 128, 1)">9</span> }</pre>
</div>
</div>
<div>在linux平台,是通过clone这个系统调用来创建的线程;值得注意的是,这个系统线程的栈是在堆上;因为其中的stk指向mp.go.stack.hi,所以g0的堆栈也就是这个系统线程的堆栈。</div>
<div>
<h4 id="tYod-1654345655262">3.3.4 M的休眠</h4>
<div>M的自旋指的是m.spining为true,这个时候它会在P的本地G队列、全局G队列、nerpoller、偷其他P的G,一直循环找可运行的G的过程中。</div>
<div>自旋状态用&nbsp;m.spinning&nbsp;和&nbsp;sched.nmspinning&nbsp;表示。其中&nbsp;m.spinning&nbsp;表示当前的M是否为自旋状态,sched.nmspinning&nbsp;表示runtime中一共有多少个M在自旋状态。</div>
<div>当自旋了一段时间后,发现仍然找不到工作,就会进入stopm函数中,使M对应的线程进行休眠。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func stopm() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">.....................
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 首先将m放到全局空闲链表中,这里要加锁访问全局链表</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">   mput(_g_.m)
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 进入睡眠状态</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   mPark()
</span><span style="color: rgba(0, 128, 128, 1)">10</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将m与p解绑</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   acquirep(_g_.m.nextp.ptr())
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    _g_.m.nextp = 0
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">14</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">func mPark() {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>    g :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)">17</span>    <span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">18</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 使工作线程休眠在park字段上</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>       notesleep(&amp;<span style="color: rgba(0, 0, 0, 1)">g.m.park)
</span><span style="color: rgba(0, 128, 128, 1)">20</span>       noteclear(&amp;<span style="color: rgba(0, 0, 0, 1)">g.m.park)
</span><span style="color: rgba(0, 128, 128, 1)">21</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> !<span style="color: rgba(0, 0, 0, 1)">mDoFixup() {
</span><span style="color: rgba(0, 128, 128, 1)">22</span>          <span style="color: rgba(0, 0, 255, 1)">return</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">25</span> }</pre>
</div>
</div>
<div>实际将线程进行休眠的代码,是通过汇编语言进行Futex系统调用来事项的。Futex机制是Linux提供的一种用户态和内核态混合的同步机制。Linux底层也是使用futex机制实现的锁。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">uaddr指向一个地址,val代表这个地址期待的值,当*uaddr==val时,才会进行wait</span>
<span style="color: rgba(0, 128, 128, 1)">2</span>      <span style="color: rgba(0, 0, 255, 1)">int</span> futex_wait(<span style="color: rgba(0, 0, 255, 1)">int</span> *uaddr, <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)"> val);
</span><span style="color: rgba(0, 128, 128, 1)">3</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">唤醒n个在uaddr指向的锁变量上挂起等待的进程</span>
<span style="color: rgba(0, 128, 128, 1)">4</span>      <span style="color: rgba(0, 0, 255, 1)">int</span> futex_wake(<span style="color: rgba(0, 0, 255, 1)">int</span> *uaddr, <span style="color: rgba(0, 0, 255, 1)">int</span> n);</pre>
</div>
</div>
<div>可以看到在futex_wait中当一个地址等于某个期待值时,就会进行wait;所以当m中的park的key等于某个值时则进入休眠状态。</div>
<h4 id="KeW4-1654345655338">3.3.5 M的唤醒</h4>
<div>M的唤醒是在wakep函数中处理的。当一个新的goroutine创建或者有多个goroutine进入_Grunnable状态时,会先判断是否有自旋的M,如果有则不会唤醒其他的M而使用自旋的M,当没有自旋的M,但m空闲队列中有空闲的M则会唤醒M,否则会创建一个新的M</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Tries to add one more P to execute G's.</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Called when a G is made runnable (newproc, ready).</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">func wakep() {
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> atomic.Load(&amp;sched.npidle) == 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>       <span style="color: rgba(0, 0, 255, 1)">return</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> be conservative about spinning threads</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果有其他的M处于自旋状态,那么就不管了,直接返回,因为自旋的M会拼命找G来运行的</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> atomic.Load(&amp;sched.nmspinning) != 0 || !atomic.Cas(&amp;sched.nmspinning, 0, 1<span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       <span style="color: rgba(0, 0, 255, 1)">return</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    startm(nil, <span style="color: rgba(0, 0, 255, 1)">true</span><span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">13</span> }</pre>
</div>
</div>
<div>startm先判断是否有空闲的P,如果没有则返回,如果有空闲的P,在尝试看看有没有空闲的M,有则唤醒该M来工作。如果没有空闲M,则新建一个M,然后也进入唤醒操作。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func startm(_p_ *<span style="color: rgba(0, 0, 0, 1)">p, spinning bool) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 禁止抢占,防止GC垃圾回收</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>    mp :=<span style="color: rgba(0, 0, 0, 1)"> acquirem()
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果P为nil,则尝试获取一个空闲P</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> _p_ ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       _p_ =<span style="color: rgba(0, 0, 0, 1)"> pidleget()
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> _p_ == nil { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 没有空闲的P,则解除禁止抢占,直接返回</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>          unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">10</span>          <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> spinning {
</span><span style="color: rgba(0, 128, 128, 1)">11</span>             <span style="color: rgba(0, 0, 255, 1)">if</span> int32(atomic.Xadd(&amp;sched.nmspinning, -1)) &lt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>                <span style="color: rgba(0, 0, 255, 1)">throw</span>("startm: negative nmspinning"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">         releasem(mp)
</span><span style="color: rgba(0, 128, 128, 1)">16</span>          <span style="color: rgba(0, 0, 255, 1)">return</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取一个空闲的M</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>    nmp :=<span style="color: rgba(0, 0, 0, 1)"> mget()
</span><span style="color: rgba(0, 128, 128, 1)">21</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> nmp ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">22</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果没有空闲的m则新建一个</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>       id :=<span style="color: rgba(0, 0, 0, 1)"> mReserveID()
</span><span style="color: rgba(0, 128, 128, 1)">24</span>       unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">25</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>       <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> fn func()
</span><span style="color: rgba(0, 128, 128, 1)">27</span>       <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> spinning {
</span><span style="color: rgba(0, 128, 128, 1)">28</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The caller incremented nmspinning, so set m.spinning in the new M.</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>          fn =<span style="color: rgba(0, 0, 0, 1)"> mspinning
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">      newm(fn, _p_, id)
</span><span style="color: rgba(0, 128, 128, 1)">32</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Ownership transfer of _p_ committed by start in newm.</span>
<span style="color: rgba(0, 128, 128, 1)">33</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Preemption is now safe.</span>
<span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">      releasem(mp)
</span><span style="color: rgba(0, 128, 128, 1)">35</span>       <span style="color: rgba(0, 0, 255, 1)">return</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">37</span>    unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">......................
</span><span style="color: rgba(0, 128, 128, 1)">39</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">标记该M是否在自旋</span>
<span style="color: rgba(0, 128, 128, 1)">40</span>    nmp.spinning =<span style="color: rgba(0, 0, 0, 1)"> spinning
</span><span style="color: rgba(0, 128, 128, 1)">41</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 暂存P</span>
<span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">   nmp.nextp.set(_p_)
</span><span style="color: rgba(0, 128, 128, 1)">43</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 唤醒M</span>
<span style="color: rgba(0, 128, 128, 1)">44</span>    notewakeup(&amp;<span style="color: rgba(0, 0, 0, 1)">nmp.park)
</span><span style="color: rgba(0, 128, 128, 1)">45</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Ownership transfer of _p_ committed by wakeup. Preemption is now</span>
<span style="color: rgba(0, 128, 128, 1)">46</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> safe.</span>
<span style="color: rgba(0, 128, 128, 1)">47</span> <span style="color: rgba(0, 0, 0, 1)">   releasem(mp)
</span><span style="color: rgba(0, 128, 128, 1)">48</span> }</pre>
</div>
</div>
<div>唤醒线程的底层操作同样是依赖futex机制</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)">1</span>      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)">唤醒n个在uaddr指向的锁变量上挂起等待的进程</span>
<span style="color: rgba(0, 128, 128, 1)">2</span>      <span style="color: rgba(0, 0, 255, 1)">int</span> futex_wake(<span style="color: rgba(0, 0, 255, 1)">int</span> *uaddr, <span style="color: rgba(0, 0, 255, 1)">int</span> n);</pre>
</div>
<div>&nbsp;</div>
<h2 id="EIWP-1654345771151">4 启动过程</h2>
<h3 id="jGjB-1654345771155">4.1 Go scheduler结构</h3>
<div>在runtime中全局变量sched代表全局调度器,数据结构为schedt结构体,保存了调度器的状态信息、全局可运行G队列</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">type schedt struct {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 用来为goroutine生成唯一id,需要以原子访问形式进行访问</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 放在struct顶部,以便在32位系统上可以进行对齐</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">   goidgen   uint64
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">...................
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">   lock mutex
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空闲的m组成的链表</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    midle      muintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> idle m's waiting for work</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空闲的工作线程数量</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>    nmidle       int32    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> number of idle m's waiting for work</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空闲的且被lock的m的数量</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>    nmidlelocked int32    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> number of locked m's waiting for work</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>    mnext      int64    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> number of m's that have been created and next M ID</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 表示最多能够创建的工作线程数量</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    maxmcount    int32    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> maximum number of m's allowed (or die)</span>
<span style="color: rgba(0, 128, 128, 1)">16</span>    nmsys      int32    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> number of system m's not counted for deadlock</span>
<span style="color: rgba(0, 128, 128, 1)">17</span>    nmfreed      int64    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> cumulative number of freed m's</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 整个goroutine的数量,能够自动保持更新</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>    ngsys uint32 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> number of system goroutines; updated atomically</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 由空闲的p结构体对象组成的链表</span>
<span style="color: rgba(0, 128, 128, 1)">21</span>    pidle      puintptr <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> idle p's</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空闲的p结构体对象的数量</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">   npidle   uint32
</span><span style="color: rgba(0, 128, 128, 1)">24</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 自旋的m的数量</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    nmspinning uint32 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> See "Worker thread parking/unparking" comment in proc.go.</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Global runnable queue.</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 全局的的g的队列</span>
<span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">   runq   gQueue
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">   runqsize int32
</span><span style="color: rgba(0, 128, 128, 1)">31</span>
<span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">   disable struct {
</span><span style="color: rgba(0, 128, 128, 1)">33</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> user disables scheduling of user goroutines.</span>
<span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">      user   bool
</span><span style="color: rgba(0, 128, 128, 1)">35</span>       runnable gQueue <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> pending runnable Gs</span>
<span style="color: rgba(0, 128, 128, 1)">36</span>       n      int32<span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> length of runnable</span>
<span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">38</span>
<span style="color: rgba(0, 128, 128, 1)">39</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Global cache of dead G's.</span>
<span style="color: rgba(0, 128, 128, 1)">40</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空闲的g队列,这里面g的状态为_Gdead</span>
<span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)">   gFree struct {
</span><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">      lock    mutex
</span><span style="color: rgba(0, 128, 128, 1)">43</span>       stack   gList <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Gs with stacks</span>
<span style="color: rgba(0, 128, 128, 1)">44</span>       noStack gList <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Gs without stacks</span>
<span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">      n       int32
</span><span style="color: rgba(0, 128, 128, 1)">46</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">47</span> <span style="color: rgba(0, 0, 0, 1)">.................
</span><span style="color: rgba(0, 128, 128, 1)">48</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 空闲的m队列</span>
<span style="color: rgba(0, 128, 128, 1)">49</span>    freem *<span style="color: rgba(0, 0, 0, 1)">m
</span><span style="color: rgba(0, 128, 128, 1)">50</span> <span style="color: rgba(0, 0, 0, 1)">.....................
</span><span style="color: rgba(0, 128, 128, 1)">51</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 上次修改gomaxprocs的时间</span>
<span style="color: rgba(0, 128, 128, 1)">52</span>    procresizetime int64 <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> nanotime() of last change to gomaxprocs</span>
<span style="color: rgba(0, 128, 128, 1)">53</span> <span style="color: rgba(0, 0, 0, 1)">......................
</span><span style="color: rgba(0, 128, 128, 1)">54</span> }</pre>
</div>
</div>
<div>这里面大部分是记录一些空闲的g、m、p等,在runtime2.go中还有很多相关的全局变量:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime/runtime2.go</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 保存所有的m</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    allm       *<span style="color: rgba(0, 0, 0, 1)">m
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> p的最大个数,默认等于cpu核数</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">   gomaxprocs int32
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> cpu的核数,程序启动时会调用osinit获取ncpu值</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">   ncpu       int32
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调度器结构体对象,记录了调度器的工作状态</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">   sched      schedt
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   newprocs   int32
</span><span style="color: rgba(0, 128, 128, 1)">12</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">   allpLock mutex
</span><span style="color: rgba(0, 128, 128, 1)">14</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 全局的p队列</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>    allp []*<span style="color: rgba(0, 0, 0, 1)">p
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">17</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime/proc.go</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> (
</span><span style="color: rgba(0, 128, 128, 1)">20</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 代表进程主线程的m0对象</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">   m0         m
</span><span style="color: rgba(0, 128, 128, 1)">22</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> m0的g0</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">   g0         g
</span><span style="color: rgba(0, 128, 128, 1)">24</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 全局的mcache对象,管理各种类型的span队列</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    mcache0      *<span style="color: rgba(0, 0, 0, 1)">mcache
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">   raceprocctx0 uintptr
</span><span style="color: rgba(0, 128, 128, 1)">27</span> )</pre>
</div>
</div>
<div>&nbsp;</div>
<h3 id="j2HF-1654345771329">4.2 启动流程</h3>
<div>调度器的初始化和启动调度循环是在进程初始化是处理的,整个进程初始化流程如下:</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604203324366-533889825.png" alt="0" data-media-type="image"></div>
<div>Go 进程的启动是通过汇编代码进行的,入口函数在asm_amd64.s这个文件中的runtime.rt0_go部分代码;</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime·rt0_go</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 程序刚启动的时候必定有一个线程启动(主线程)</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将当前的栈和资源保存在g0</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将该线程保存在m0</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> tls: Thread Local Storage</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> set the per-goroutine and per-mach "registers"</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">get_tls(BX)
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">LEAQ    runtime·g0(SB), CX
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">MOVQ    CX, g(BX)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">LEAQ    runtime·m0(SB), AX
</span><span style="color: rgba(0, 128, 128, 1)">12</span>
<span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> m0和g0互相绑定</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> save m-&gt;g0 = g0</span>
<span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">MOVQ    CX, m_g0(AX)
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> save m0 to g0-&gt;m</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">MOVQ    AX, g_m(CX)
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 处理args</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">CALL    runtime·args(SB)
</span><span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> os初始化, os_linux.go</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">CALL    runtime·osinit(SB)
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调度系统初始化, proc.go</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">CALL    runtime·schedinit(SB)
</span><span style="color: rgba(0, 128, 128, 1)">24</span>
<span style="color: rgba(0, 128, 128, 1)">25</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 创建一个goroutine,然后开启执行程序</span>
<span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> create a new goroutine to start program</span>
<span style="color: rgba(0, 128, 128, 1)">27</span> MOVQ    $runtime·mainPC(SB), AX      <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> entry</span>
<span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">PUSHQ    AX
</span><span style="color: rgba(0, 128, 128, 1)">29</span> PUSHQ    $0            <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> arg size</span>
<span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">CALL    runtime·newproc(SB)
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">POPQ    AX
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">POPQ    AX
</span><span style="color: rgba(0, 128, 128, 1)">33</span>
<span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> start this M</span>
<span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 启动线程,并且启动调度系统</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> CALL    runtime·mstart(SB)</pre>
</div>
</div>
<div>可以看到通过汇编代码:</div>
<div>1、将m0与g0互相绑定,然后调用runtime.osinit函数,这个函数不同的操作系统有不同的实现;</div>
<div>2、然后调用runtime.schedinit进行调度系统的初始化;</div>
<div>3、然后创建主协程;主goroutine创建完成后被加入到p的运行队列中,等待调度;</div>
<div>4、在g0上启动调用runtime.mstart启动调度循环,首先可以被调度执行的就是主goroutine,然后主协程获得运行的cpu则执行runtime.main进而执行到用户代码的main函数。</div>
<div>&nbsp;</div>
<div>初始化过程和堆栈图可以参考下图:</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604203324682-715411035.png" alt="0" data-media-type="image"></div>
<div>堆栈上,g0、m0、p0与主协程关系如图所示:</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604203324607-584302294.png" alt="0" data-media-type="image"></div>
<h3 id="unUk-1654345771432">4.3 调度器初始化</h3>
<div>调度器初始化</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func schedinit() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">................
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> g0</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> raceenabled {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       _g_.racectx, raceprocctx0 =<span style="color: rgba(0, 0, 0, 1)"> raceinit()
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 最多启动10000个工作线程</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>    sched.maxmcount = 10000
<span style="color: rgba(0, 128, 128, 1)">10</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The world starts stopped.</span>
<span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">   worldStopped()
</span><span style="color: rgba(0, 128, 128, 1)">13</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> <span style="color: rgba(0, 0, 0, 1)">   moduledataverify()
</span><span style="color: rgba(0, 128, 128, 1)">15</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化协程堆栈,包括专门分配小栈的stackpool和分配大栈的stackLarge</span>
<span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">   stackinit()
</span><span style="color: rgba(0, 128, 128, 1)">17</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 整个堆内存的初始化分配</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   mallocinit()
</span><span style="color: rgba(0, 128, 128, 1)">19</span>    fastrandinit() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> must run before mcommoninit</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化m0</span>
<span style="color: rgba(0, 128, 128, 1)">21</span>    mcommoninit(_g_.m, -1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">22</span>    cpuinit()       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> must run before alginit</span>
<span style="color: rgba(0, 128, 128, 1)">23</span>    alginit()       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> maps must not be used before this call</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>    modulesinit()   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> provides activeModules</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    typelinksinit() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> uses maps, activeModules</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>    itabsinit()   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> uses activeModules</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>    sigsave(&amp;<span style="color: rgba(0, 0, 0, 1)">_g_.m.sigmask)
</span><span style="color: rgba(0, 128, 128, 1)">29</span>    initSigmask =<span style="color: rgba(0, 0, 0, 1)"> _g_.m.sigmask
</span><span style="color: rgba(0, 128, 128, 1)">30</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> offset := unsafe.Offsetof(sched.timeToRun); offset%8 != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">32</span> <span style="color: rgba(0, 0, 0, 1)">      println(offset)
</span><span style="color: rgba(0, 128, 128, 1)">33</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("sched.timeToRun not aligned to 8 bytes"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">35</span>
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">   goargs()
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">   goenvs()
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">   parsedebugvars()
</span><span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)">   gcinit()
</span><span style="color: rgba(0, 128, 128, 1)">40</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这部分是初始化p,</span>
<span style="color: rgba(0, 128, 128, 1)">41</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> cpu有多少个核数就初始化多少个p</span>
<span style="color: rgba(0, 128, 128, 1)">42</span>    lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">43</span>    sched.lastpoll =<span style="color: rgba(0, 0, 0, 1)"> uint64(nanotime())
</span><span style="color: rgba(0, 128, 128, 1)">44</span>    procs :=<span style="color: rgba(0, 0, 0, 1)"> ncpu
</span><span style="color: rgba(0, 128, 128, 1)">45</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> n, ok := atoi32(gogetenv("GOMAXPROCS")); ok &amp;&amp; n &gt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">46</span>       procs =<span style="color: rgba(0, 0, 0, 1)"> n
</span><span style="color: rgba(0, 128, 128, 1)">47</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">48</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> procresize(procs) !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">49</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("unknown runnable goroutine during bootstrap"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">50</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">51</span>    unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">52</span>
<span style="color: rgba(0, 128, 128, 1)">53</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> World is effectively started now, as P's can run.</span>
<span style="color: rgba(0, 128, 128, 1)">54</span> <span style="color: rgba(0, 0, 0, 1)">   worldStarted()
</span><span style="color: rgba(0, 128, 128, 1)">55</span> }</pre>
</div>
</div>
<h3 id="2kEM-1654345771550">4.4 启动调度系统</h3>
<div>调度系统时在runtime.mstart0函数中启动的。这个函数是在m0的g0上执行的。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func mstart0() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 这里获取的是g0在系统栈上执行</span>
<span style="color: rgba(0, 128, 128, 1)"> 3</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">.............
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">   mstart1()
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 0, 0, 1)">.............
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">func mstart1(dummy int32) {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>   _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)">11</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 确保g是系统栈上的g0</span>
<span style="color: rgba(0, 128, 128, 1)">12</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 调度器只在g0上执行</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> _g_ !=<span style="color: rgba(0, 0, 0, 1)"> _g_.m.g0 {
</span><span style="color: rgba(0, 128, 128, 1)">14</span>         <span style="color: rgba(0, 0, 255, 1)">throw</span>("bad runtime·mstart"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">17</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 初始化m,主要是设置线程的备用信号堆栈和信号掩码</span>
<span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">    minit()
</span><span style="color: rgba(0, 128, 128, 1)">19</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果当前g的m是初始m0,执行mstartm0()</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m == &amp;<span style="color: rgba(0, 0, 0, 1)">m0 {
</span><span style="color: rgba(0, 128, 128, 1)">21</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对于初始m,需要一些特殊处理,主要是设置系统信号量的处理函数</span>
<span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">      mstartm0()
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">24</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果有m的起始任务函数,则执行,比如 sysmon 函数</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 对于m0来说,是没有 mstartfn 的</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> fn := _g_.m.mstartfn; fn !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">      fn()
</span><span style="color: rgba(0, 128, 128, 1)">28</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">29</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m.helpgc != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">30</span>         _g_.m.helpgc = 0
<span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">      stopm()
</span><span style="color: rgba(0, 128, 128, 1)">32</span>   } <span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m != &amp;m0 { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果不是m0,需要绑定p</span>
<span style="color: rgba(0, 128, 128, 1)">33</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 绑定p</span>
<span style="color: rgba(0, 128, 128, 1)">34</span> <span style="color: rgba(0, 0, 0, 1)">      acquirep(_g_.m.nextp.ptr())
</span><span style="color: rgba(0, 128, 128, 1)">35</span>         _g_.m.nextp = 0
<span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">37</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 进入调度循环,永不返回</span>
<span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">    schedule()
</span><span style="color: rgba(0, 128, 128, 1)">39</span> }</pre>
</div>
</div>
<div>&nbsp;</div>
<h3 id="u7J2-1654345771635">4.5 runtime.main函数</h3>
<div>当经过初始的调度,主协程获取执行权后,首先进入的就是runtime.main函数:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> The main goroutine.</span>
<span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">func main() {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 获取 main goroutine</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span>   g :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 在系统栈上运行 sysmon</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> <span style="color: rgba(0, 0, 0, 1)">    systemstack(func() {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 分配一个新的m,运行sysmon系统后台监控</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (定期垃圾回收和调度抢占)</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">      newm(sysmon, nil)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">    })
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 确保是主线程</span>
<span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> g.m != &amp;<span style="color: rgba(0, 0, 0, 1)">m0 {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>         <span style="color: rgba(0, 0, 255, 1)">throw</span>("runtime.main not on m0"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">16</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">17</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> runtime 内部 init 函数的执行,编译器动态生成的。</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>   runtime_init() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> must be before defer</span>
<span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">20</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> gc 启动一个goroutine进行gc清扫</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">    gcenable()
</span><span style="color: rgba(0, 128, 128, 1)">22</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">23</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 执行init函数,编译器动态生成的,</span>
<span style="color: rgba(0, 128, 128, 1)">24</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 包括用户定义的所有的init函数。</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> make an indirect call,</span>
<span style="color: rgba(0, 128, 128, 1)">26</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> as the linker doesn't know the address of</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the main package when laying down the runtime</span>
<span style="color: rgba(0, 128, 128, 1)">28</span>   fn :=<span style="color: rgba(0, 0, 0, 1)"> main_init
</span><span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">    fn()
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">31</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 真正的执行main func in package main</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> make an indirect call,</span>
<span style="color: rgba(0, 128, 128, 1)">33</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> as the linker doesn't know the address of</span>
<span style="color: rgba(0, 128, 128, 1)">34</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> the main package when laying down the runtime</span>
<span style="color: rgba(0, 128, 128, 1)">35</span>   fn =<span style="color: rgba(0, 0, 0, 1)"> main_main
</span><span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">    fn()
</span><span style="color: rgba(0, 128, 128, 1)">37</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">38</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 退出程序</span>
<span style="color: rgba(0, 128, 128, 1)">39</span>   exit(0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">40</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为何这里还需要for循环?</span>
<span style="color: rgba(0, 128, 128, 1)">41</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 下面的for循环一定会导致程序崩掉,这样就确保了程序一定会退出</span>
<span style="color: rgba(0, 128, 128, 1)">42</span>   <span style="color: rgba(0, 0, 255, 1)">for</span><span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">43</span>         <span style="color: rgba(0, 0, 255, 1)">var</span> x *<span style="color: rgba(0, 0, 0, 1)">int32
</span><span style="color: rgba(0, 128, 128, 1)">44</span>         *x = 0
<span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">46</span> }</pre>
</div>
</div>
<div>runtime.main函数中需要注意的是在系统栈上创建了一个新的m,来执行sysmon协程,这个协程是用来定期执行垃圾回收和调度抢占。</div>
<div>其中可以看到首先获取了main_init函数,来执行runtime包中的init函数,然后是获取main_main来执行用户的main函数。</div>
<div>接着main函数执行完成后,调用exit让主进程退出,如果进程没有退出,就让for循环一直访问非法地址,让操作系统把进程杀死,这样来确保进程一定会退出。</div>
<div>&nbsp;</div>
</div>
<div>
<h2 id="AR16-1654346168824">5 协程的调度策略</h2>
<div>调度循环启动之后,便会进入一个无限循环中,不断的执行schedule-&gt;execute-&gt;gogo-&gt;goroutine任务-&gt;goexit-&gt;goexit1-&gt;mcall-&gt;goexit0-&gt;schedule;</div>
<div>其中调度的过程是在m的g0上执行的,而goroutine任务-&gt;goexit-&gt;goexit1-&gt;mcall则是在goroutine的堆栈空间上执行的。</div>
<h3 id="JLdS-1654346168832">5.1 调度策略</h3>
<div>其中schedule函数处理具体的调度策略;execute函数执行一些具体的状态转移、协程g与结构体m之间的绑定;gogo函数是与操作系统相关的函数,执行汇编代码完成栈的切换以及CPU寄存器的恢复。</div>
<div>先来看下schedule的代码:</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func schedule() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>   _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">top:
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果当前GC需要停止整个世界(STW), 则调用gcstopm休眠当前的M</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> sched.gcwaiting != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为了STW,停止当前的M</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">      gcstopm()
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> STW结束后回到 top</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">      goto top
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">13</span>   <span style="color: rgba(0, 0, 255, 1)">var</span> gp *<span style="color: rgba(0, 0, 0, 1)">g
</span><span style="color: rgba(0, 128, 128, 1)">14</span>   <span style="color: rgba(0, 0, 255, 1)">var</span><span style="color: rgba(0, 0, 0, 1)"> inheritTime bool
</span><span style="color: rgba(0, 128, 128, 1)">15</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">16</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> gp ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">17</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 为了防止全局队列中的g永远得不到运行,所以go语言让p每执行61调度,</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>               <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 就需要优先从全局队列中获取一个G到当前P中,并执行下一个要执行的G</span>
<span style="color: rgba(0, 128, 128, 1)">19</span>         <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m.p.ptr().schedtick%61 == 0 &amp;&amp; sched.runqsize &gt; 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">20</span>             lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">21</span>             gp = globrunqget(_g_.m.p.ptr(), 1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">22</span>             unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">25</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> gp ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从p的本地队列中获取</span>
<span style="color: rgba(0, 128, 128, 1)">27</span>         gp, inheritTime =<span style="color: rgba(0, 0, 0, 1)"> runqget(_g_.m.p.ptr())
</span><span style="color: rgba(0, 128, 128, 1)">28</span>         <span style="color: rgba(0, 0, 255, 1)">if</span> gp != nil &amp;&amp;<span style="color: rgba(0, 0, 0, 1)"> _g_.m.spinning {
</span><span style="color: rgba(0, 128, 128, 1)">29</span>             <span style="color: rgba(0, 0, 255, 1)">throw</span>("schedule: spinning with local work"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">31</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">32</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> gp ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">33</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 想尽办法找到可运行的G,找不到就不用返回了</span>
<span style="color: rgba(0, 128, 128, 1)">34</span>         gp, inheritTime = findrunnable() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> blocks until work is available</span>
<span style="color: rgba(0, 128, 128, 1)">35</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">36</span> <span style="color: rgba(0, 0, 0, 1)">    ...
</span><span style="color: rgba(0, 128, 128, 1)">37</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> println("execute goroutine", gp.goid)</span>
<span style="color: rgba(0, 128, 128, 1)">38</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 找到了g,那就执行g上的任务函数</span>
<span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)">    execute(gp, inheritTime)
</span><span style="color: rgba(0, 128, 128, 1)">40</span> }</pre>
</div>
</div>
<div>findrunnalbe中首先还是从本地队列中检查,然后从全局队列中寻找,再从就绪的网络协程,如果这几个没有就去其他p的本地队列偷一些任务。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func findrunnable() (gp *<span style="color: rgba(0, 0, 0, 1)">g, inheritTime bool) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">top:
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span> <span style="color: rgba(0, 0, 0, 1)">............................
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 本地队列中检查</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> gp, inheritTime := runqget(_p_); gp !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>       <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> gp, inheritTime
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">10</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从全局队列中寻找</span>
<span style="color: rgba(0, 128, 128, 1)">11</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> sched.runqsize != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       gp := globrunqget(_p_, 0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">14</span>       unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">15</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> gp !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">16</span>          <span style="color: rgba(0, 0, 255, 1)">return</span> gp, <span style="color: rgba(0, 0, 255, 1)">false</span>
<span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从就绪的网络协程中查找</span>
<span style="color: rgba(0, 128, 128, 1)">20</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> netpollinited() &amp;&amp; atomic.Load(&amp;netpollWaiters) &gt; 0 &amp;&amp; atomic.Load64(&amp;sched.lastpoll) != 0<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)">21</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> list := netpoll(0); !list.empty() { <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> non-blocking</span>
<span style="color: rgba(0, 128, 128, 1)">22</span>          gp :=<span style="color: rgba(0, 0, 0, 1)"> list.pop()
</span><span style="color: rgba(0, 128, 128, 1)">23</span>          injectglist(&amp;<span style="color: rgba(0, 0, 0, 1)">list)
</span><span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">         casgstatus(gp, _Gwaiting, _Grunnable)
</span><span style="color: rgba(0, 128, 128, 1)">25</span>          <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> trace.enabled {
</span><span style="color: rgba(0, 128, 128, 1)">26</span>             traceGoUnpark(gp, 0<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">28</span>          <span style="color: rgba(0, 0, 255, 1)">return</span> gp, <span style="color: rgba(0, 0, 255, 1)">false</span>
<span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">30</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">31</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 进入自旋状态</span>
<span style="color: rgba(0, 128, 128, 1)">33</span>   procs :=<span style="color: rgba(0, 0, 0, 1)"> uint32(gomaxprocs)
</span><span style="color: rgba(0, 128, 128, 1)">34</span>   <span style="color: rgba(0, 0, 255, 1)">if</span> _g_.m.spinning || 2*atomic.Load(&amp;sched.nmspinning) &lt; procs-atomic.Load(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.npidle) {
</span><span style="color: rgba(0, 128, 128, 1)">35</span>      <span style="color: rgba(0, 0, 255, 1)">if</span> !<span style="color: rgba(0, 0, 0, 1)">_g_.m.spinning {
</span><span style="color: rgba(0, 128, 128, 1)">36</span>         _g_.m.spinning = <span style="color: rgba(0, 0, 255, 1)">true</span>
<span style="color: rgba(0, 128, 128, 1)">37</span>         atomic.Xadd(&amp;sched.nmspinning, 1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">       }
</span><span style="color: rgba(0, 128, 128, 1)">39</span>         <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 从其他p的本地队列中偷工作</span>
<span style="color: rgba(0, 128, 128, 1)">40</span>      gp, inheritTime, tnow, w, newWork :=<span style="color: rgba(0, 0, 0, 1)"> stealWork(now)
</span><span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)">..............................
</span><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">    }
</span><span style="color: rgba(0, 128, 128, 1)">43</span> }</pre>
</div>
</div>
<div>整个调度的优先级如下:</div>
<div><img src="https://img2022.cnblogs.com/blog/412020/202206/412020-20220604203906251-1312367763.png" alt="0" data-media-type="image"></div>
<div>&nbsp;</div>
<h3 id="2NuR-1654346169015">5.2 调度时机</h3>
<div>5.1讲了调度的策略,什么时机发生调度呢,主要有三种方式,主动调度、被动调度、抢占式调度。</div>
<h4 id="cb3A-1654346169021">5.2.1 主动调度</h4>
<div>协程可以选择主动让渡自己的执行权利,大多数情况下不需要这么做,但通过runtime.Goched可以做到主动让渡。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func Gosched() {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">   checkTimeouts()
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span> <span style="color: rgba(0, 0, 0, 1)">   mcall(gosched_m)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Gosched continuation on g0.</span>
<span style="color: rgba(0, 128, 128, 1)"> 7</span> func gosched_m(gp *<span style="color: rgba(0, 0, 0, 1)">g) {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>    <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> trace.enabled {
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span> <span style="color: rgba(0, 0, 0, 1)">      traceGoSched()
</span><span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   goschedImpl(gp)
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">13</span>
<span style="color: rgba(0, 128, 128, 1)">14</span> func goschedImpl(gp *<span style="color: rgba(0, 0, 0, 1)">g) {
</span><span style="color: rgba(0, 128, 128, 1)">15</span>    status :=<span style="color: rgba(0, 0, 0, 1)"> readgstatus(gp)
</span><span style="color: rgba(0, 128, 128, 1)">16</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> status&amp;^_Gscan !=<span style="color: rgba(0, 0, 0, 1)"> _Grunning {
</span><span style="color: rgba(0, 128, 128, 1)">17</span> <span style="color: rgba(0, 0, 0, 1)">      dumpgstatus(gp)
</span><span style="color: rgba(0, 128, 128, 1)">18</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("bad g status"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">19</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">20</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 更改g的运行状态</span>
<span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">   casgstatus(gp, _Grunning, _Grunnable)
</span><span style="color: rgba(0, 128, 128, 1)">22</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接触g和m的绑定关系</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 0, 0, 1)">   dropg()
</span><span style="color: rgba(0, 128, 128, 1)">24</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 将g放入全局队列中</span>
<span style="color: rgba(0, 128, 128, 1)">25</span>    lock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">   globrunqput(gp)
</span><span style="color: rgba(0, 128, 128, 1)">27</span>    unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">sched.lock)
</span><span style="color: rgba(0, 128, 128, 1)">28</span>
<span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">   schedule()
</span><span style="color: rgba(0, 128, 128, 1)">30</span> }</pre>
</div>
</div>
<h4 id="TKq9-1654346169088">5.2.2 被动调度</h4>
<div>大部分情况下的调度都是被动调度,当协程在休眠、channel通道阻塞、网络IO阻塞、执行垃圾回收时会暂停,被动调度可以保证最大化利用CPU资源。被动调度是协程发起的操作,所以调度时机相对明确。</div>
<div>首先从当前栈切换到g0协程,被动调度不会将G放入全局运行队列,所以被动调度需要一个额外的唤醒机制。</div>
<div>这里面涉及的函数主要是gopark和ready函数。</div>
<div>gopark函数用来完成被动调度,有_Grunning变为_Gwaiting状态;</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func gopark(unlockf func(*g, unsafe.Pointer) bool, lock unsafe.Pointer, reason waitReason, traceEv <span style="color: rgba(0, 0, 255, 1)">byte</span>, traceskip <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> reason !=<span style="color: rgba(0, 0, 0, 1)"> waitReasonSleep {
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>       checkTimeouts() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> timeouts may expire while two goroutines keep the scheduler busy</span>
<span style="color: rgba(0, 128, 128, 1)"> 4</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 禁止抢占和GC</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    mp :=<span style="color: rgba(0, 0, 0, 1)"> acquirem()
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    gp := mp.curg <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 过去当前m上运行的g</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span>    status :=<span style="color: rgba(0, 0, 0, 1)"> readgstatus(gp)
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> status != _Grunning &amp;&amp; status !=<span style="color: rgba(0, 0, 0, 1)"> _Gscanrunning {
</span><span style="color: rgba(0, 128, 128, 1)">10</span>       <span style="color: rgba(0, 0, 255, 1)">throw</span>("gopark: bad g status"<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">12</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 设置相关的wait字段</span>
<span style="color: rgba(0, 128, 128, 1)">13</span>    mp.waitlock =<span style="color: rgba(0, 0, 0, 1)"> lock
</span><span style="color: rgba(0, 128, 128, 1)">14</span>    mp.waitunlockf =<span style="color: rgba(0, 0, 0, 1)"> unlockf
</span><span style="color: rgba(0, 128, 128, 1)">15</span>    gp.waitreason =<span style="color: rgba(0, 0, 0, 1)"> reason
</span><span style="color: rgba(0, 128, 128, 1)">16</span>    mp.waittraceev =<span style="color: rgba(0, 0, 0, 1)"> traceEv
</span><span style="color: rgba(0, 128, 128, 1)">17</span>    mp.waittraceskip =<span style="color: rgba(0, 0, 0, 1)"> traceskip
</span><span style="color: rgba(0, 128, 128, 1)">18</span> <span style="color: rgba(0, 0, 0, 1)">   releasem(mp)
</span><span style="color: rgba(0, 128, 128, 1)">19</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> can't do anything that might move the G between Ms here.</span>
<span style="color: rgba(0, 128, 128, 1)">20</span> <span style="color: rgba(0, 0, 0, 1)">   mcall(park_m)
</span><span style="color: rgba(0, 128, 128, 1)">21</span> <span style="color: rgba(0, 0, 0, 1)">}
</span><span style="color: rgba(0, 128, 128, 1)">22</span>
<span style="color: rgba(0, 128, 128, 1)">23</span> <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> park continuation on g0.</span>
<span style="color: rgba(0, 128, 128, 1)">24</span> func park_m(gp *<span style="color: rgba(0, 0, 0, 1)">g) {
</span><span style="color: rgba(0, 128, 128, 1)">25</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)">26</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 变更g的状态</span>
<span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">   casgstatus(gp, _Grunning, _Gwaiting)
</span><span style="color: rgba(0, 128, 128, 1)">28</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 接触g与m的绑定关系</span>
<span style="color: rgba(0, 128, 128, 1)">29</span> <span style="color: rgba(0, 0, 0, 1)">   dropg()
</span><span style="color: rgba(0, 128, 128, 1)">30</span>   <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 根据被动调度不同原因,执行不同的waitunlockf函数</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>    <span style="color: rgba(0, 0, 255, 1)">if</span> fn := _g_.m.waitunlockf; fn !=<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)">32</span>       ok :=<span style="color: rgba(0, 0, 0, 1)"> fn(gp, _g_.m.waitlock)
</span><span style="color: rgba(0, 128, 128, 1)">33</span>       _g_.m.waitunlockf =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">34</span>       _g_.m.waitlock =<span style="color: rgba(0, 0, 0, 1)"> nil
</span><span style="color: rgba(0, 128, 128, 1)">35</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> !<span style="color: rgba(0, 0, 0, 1)">ok {
</span><span style="color: rgba(0, 128, 128, 1)">36</span>          <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> trace.enabled {
</span><span style="color: rgba(0, 128, 128, 1)">37</span>             traceGoUnpark(gp, 2<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">38</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)">         casgstatus(gp, _Gwaiting, _Grunnable)
</span><span style="color: rgba(0, 128, 128, 1)">40</span>          execute(gp, <span style="color: rgba(0, 0, 255, 1)">true</span>) <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Schedule it back, never returns.</span>
<span style="color: rgba(0, 128, 128, 1)">41</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">42</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">43</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 进入下一轮调度</span>
<span style="color: rgba(0, 128, 128, 1)">44</span> <span style="color: rgba(0, 0, 0, 1)">   schedule()
</span><span style="color: rgba(0, 128, 128, 1)">45</span> }</pre>
</div>
</div>
<div>当协程要被唤醒时,会进入ready函数中,更改协程状态由_Gwaiting变更为_Grunnable,放入本地运行队列等待被调度。</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> func ready(gp *g, traceskip <span style="color: rgba(0, 0, 255, 1)">int</span><span style="color: rgba(0, 0, 0, 1)">, next bool) {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span> <span style="color: rgba(0, 0, 0, 1)">..............
</span><span style="color: rgba(0, 128, 128, 1)"> 3</span>    status :=<span style="color: rgba(0, 0, 0, 1)"> readgstatus(gp)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Mark runnable.</span>
<span style="color: rgba(0, 128, 128, 1)"> 6</span>    _g_ :=<span style="color: rgba(0, 0, 0, 1)"> getg()
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>    mp := acquirem() <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> disable preemption because it can be holding p in a local var</span>
<span style="color: rgba(0, 128, 128, 1)"> 8</span> <span style="color: rgba(0, 0, 0, 1)">...............
</span><span style="color: rgba(0, 128, 128, 1)"> 9</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 变更状态之后,放入p的局部运行队列中</span>
<span style="color: rgba(0, 128, 128, 1)">10</span> <span style="color: rgba(0, 0, 0, 1)">   casgstatus(gp, _Gwaiting, _Grunnable)
</span><span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">   runqput(_g_.m.p.ptr(), gp, next)
</span><span style="color: rgba(0, 128, 128, 1)">12</span> <span style="color: rgba(0, 0, 0, 1)">   wakep()
</span><span style="color: rgba(0, 128, 128, 1)">13</span> <span style="color: rgba(0, 0, 0, 1)">   releasem(mp)
</span><span style="color: rgba(0, 128, 128, 1)">14</span> }</pre>
</div>
</div>
<h4 id="zTqm-1654346169226">5.2.3 抢占式调度</h4>
<div>如果一个g运行时间过长就会导致其他g难以获取运行机会,当进行系统调用时也存在会导致其他g无法运行情况;当出现这两种情况时,为了让其他g有运行机会,则会进行抢占式调度。</div>
<div>是否进行抢占式调度主要是在sysmon协程上判断的。sysmon协程是一个不需要p的协程,它作用主要是运行后台监控,<strong>进行netpool(获取fd事件)、retake(抢占式调度)、forcegc(按时间强制执行GC)、scavenge heap(强制释放闲置堆内存,减少内存占用)</strong></div>
<div>&nbsp;</div>
<div>其中与抢占式调度相关的就是retake函数,</div>
<div>这里我们需要知道的是连续运行10ms则认为时间过长,进行抢占</div>
<div>发生系统调用时,当前正在工作的线程会陷入等待状态,等待内部完成系统调用并返回,所以也需要让渡执行权给其他g,但这里当只有满足几种情况才会进行调度:</div>
<div>1、如果p的运行队列中有等待运行的g则抢占</div>
<div>2、如果没有空闲的p则进行抢占</div>
<div>3、系统调用时间超过10ms则进行抢占</div>
<div data-theme="default" data-language="">
<div class="cnblogs_code">
<pre><span style="color: rgba(0, 128, 128, 1)"> 1</span> <span style="color: rgba(0, 0, 0, 1)">func retake(now int64) uint32 {
</span><span style="color: rgba(0, 128, 128, 1)"> 2</span>    n := 0
<span style="color: rgba(0, 128, 128, 1)"> 3</span>    lock(&amp;<span style="color: rgba(0, 0, 0, 1)">allpLock)
</span><span style="color: rgba(0, 128, 128, 1)"> 4</span>    <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 遍历所有的P</span>
<span style="color: rgba(0, 128, 128, 1)"> 5</span>    <span style="color: rgba(0, 0, 255, 1)">for</span> i := 0; i &lt; len(allp); i++<span style="color: rgba(0, 0, 0, 1)"> {
</span><span style="color: rgba(0, 128, 128, 1)"> 6</span>       _p_ :=<span style="color: rgba(0, 0, 0, 1)"> allp
</span><span style="color: rgba(0, 128, 128, 1)"> 7</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> _p_ ==<span style="color: rgba(0, 0, 0, 1)"> nil {
</span><span style="color: rgba(0, 128, 128, 1)"> 8</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> This can happen if procresize has grown</span>
<span style="color: rgba(0, 128, 128, 1)"> 9</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> allp but not yet created new Ps.</span>
<span style="color: rgba(0, 128, 128, 1)">10</span>          <span style="color: rgba(0, 0, 255, 1)">continue</span>
<span style="color: rgba(0, 128, 128, 1)">11</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">12</span>       pd := &amp;<span style="color: rgba(0, 0, 0, 1)">_p_.sysmontick
</span><span style="color: rgba(0, 128, 128, 1)">13</span>       s :=<span style="color: rgba(0, 0, 0, 1)"> _p_.status
</span><span style="color: rgba(0, 128, 128, 1)">14</span>       sysretake := <span style="color: rgba(0, 0, 255, 1)">false</span>
<span style="color: rgba(0, 128, 128, 1)">15</span>      
<span style="color: rgba(0, 128, 128, 1)">16</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> s == _Prunning || s ==<span style="color: rgba(0, 0, 0, 1)"> _Psyscall {
</span><span style="color: rgba(0, 128, 128, 1)">17</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 判断如果g的运行时间过长则抢占</span>
<span style="color: rgba(0, 128, 128, 1)">18</span>          t :=<span style="color: rgba(0, 0, 0, 1)"> int64(_p_.schedtick)
</span><span style="color: rgba(0, 128, 128, 1)">19</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> int64(pd.schedtick) !=<span style="color: rgba(0, 0, 0, 1)"> t {
</span><span style="color: rgba(0, 128, 128, 1)">20</span>             pd.schedtick =<span style="color: rgba(0, 0, 0, 1)"> uint32(t)
</span><span style="color: rgba(0, 128, 128, 1)">21</span>             pd.schedwhen =<span style="color: rgba(0, 0, 0, 1)"> now
</span><span style="color: rgba(0, 128, 128, 1)">22</span>          } <span style="color: rgba(0, 0, 255, 1)">else</span> <span style="color: rgba(0, 0, 255, 1)">if</span> pd.schedwhen+forcePreemptNS &lt;=<span style="color: rgba(0, 0, 0, 1)"> now {
</span><span style="color: rgba(0, 128, 128, 1)">23</span>            <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果连续运行10ms则进行抢占</span>
<span style="color: rgba(0, 128, 128, 1)">24</span> <span style="color: rgba(0, 0, 0, 1)">            preemptone(_p_)
</span><span style="color: rgba(0, 128, 128, 1)">25</span>             sysretake = <span style="color: rgba(0, 0, 255, 1)">true</span>
<span style="color: rgba(0, 128, 128, 1)">26</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">27</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">28</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 针对系统调用情况进行抢占</span>
<span style="color: rgba(0, 128, 128, 1)">29</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果p的运行队列中有等待运行的g则抢占</span>
<span style="color: rgba(0, 128, 128, 1)">30</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 如果没有空闲的p则进行抢占</span>
<span style="color: rgba(0, 128, 128, 1)">31</span>       <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> 系统调用时间超过10ms则进行抢占</span>
<span style="color: rgba(0, 128, 128, 1)">32</span>       <span style="color: rgba(0, 0, 255, 1)">if</span> s ==<span style="color: rgba(0, 0, 0, 1)"> _Psyscall {
</span><span style="color: rgba(0, 128, 128, 1)">33</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Retake P from syscall if it's there for more than 1 sysmon tick (at least 20us).</span>
<span style="color: rgba(0, 128, 128, 1)">34</span>          t :=<span style="color: rgba(0, 0, 0, 1)"> int64(_p_.syscalltick)
</span><span style="color: rgba(0, 128, 128, 1)">35</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> !sysretake &amp;&amp; int64(pd.syscalltick) !=<span style="color: rgba(0, 0, 0, 1)"> t {
</span><span style="color: rgba(0, 128, 128, 1)">36</span>             pd.syscalltick =<span style="color: rgba(0, 0, 0, 1)"> uint32(t)
</span><span style="color: rgba(0, 128, 128, 1)">37</span>             pd.syscallwhen =<span style="color: rgba(0, 0, 0, 1)"> now
</span><span style="color: rgba(0, 128, 128, 1)">38</span>             <span style="color: rgba(0, 0, 255, 1)">continue</span>
<span style="color: rgba(0, 128, 128, 1)">39</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">40</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> On the one hand we don't want to retake Ps if there is no other work to do,</span>
<span style="color: rgba(0, 128, 128, 1)">41</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> but on the other hand we want to retake them eventually</span>
<span style="color: rgba(0, 128, 128, 1)">42</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> because they can prevent the sysmon thread from deep sleep.</span>
<span style="color: rgba(0, 128, 128, 1)">43</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> runqempty(_p_) &amp;&amp; atomic.Load(&amp;sched.nmspinning)+atomic.Load(&amp;sched.npidle) &gt; 0 &amp;&amp; pd.syscallwhen+10*1000*1000 &gt;<span style="color: rgba(0, 0, 0, 1)"> now {
</span><span style="color: rgba(0, 128, 128, 1)">44</span>             <span style="color: rgba(0, 0, 255, 1)">continue</span>
<span style="color: rgba(0, 128, 128, 1)">45</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">46</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Drop allpLock so we can take sched.lock.</span>
<span style="color: rgba(0, 128, 128, 1)">47</span>          unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">allpLock)
</span><span style="color: rgba(0, 128, 128, 1)">48</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Need to decrement number of idle locked M's</span>
<span style="color: rgba(0, 128, 128, 1)">49</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> (pretending that one more is running) before the CAS.</span>
<span style="color: rgba(0, 128, 128, 1)">50</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> Otherwise the M from which we retake can exit the syscall,</span>
<span style="color: rgba(0, 128, 128, 1)">51</span>          <span style="color: rgba(0, 128, 0, 1)">//</span><span style="color: rgba(0, 128, 0, 1)"> increment nmidle and report deadlock.</span>
<span style="color: rgba(0, 128, 128, 1)">52</span>          incidlelocked(-1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">53</span>          <span style="color: rgba(0, 0, 255, 1)">if</span> atomic.Cas(&amp;<span style="color: rgba(0, 0, 0, 1)">_p_.status, s, _Pidle) {
</span><span style="color: rgba(0, 128, 128, 1)">54</span>             <span style="color: rgba(0, 0, 255, 1)">if</span><span style="color: rgba(0, 0, 0, 1)"> trace.enabled {
</span><span style="color: rgba(0, 128, 128, 1)">55</span> <span style="color: rgba(0, 0, 0, 1)">               traceGoSysBlock(_p_)
</span><span style="color: rgba(0, 128, 128, 1)">56</span> <span style="color: rgba(0, 0, 0, 1)">               traceProcStop(_p_)
</span><span style="color: rgba(0, 128, 128, 1)">57</span> <span style="color: rgba(0, 0, 0, 1)">            }
</span><span style="color: rgba(0, 128, 128, 1)">58</span>             n++
<span style="color: rgba(0, 128, 128, 1)">59</span>             _p_.syscalltick++
<span style="color: rgba(0, 128, 128, 1)">60</span> <span style="color: rgba(0, 0, 0, 1)">            handoffp(_p_)
</span><span style="color: rgba(0, 128, 128, 1)">61</span> <span style="color: rgba(0, 0, 0, 1)">         }
</span><span style="color: rgba(0, 128, 128, 1)">62</span>          incidlelocked(1<span style="color: rgba(0, 0, 0, 1)">)
</span><span style="color: rgba(0, 128, 128, 1)">63</span>          lock(&amp;<span style="color: rgba(0, 0, 0, 1)">allpLock)
</span><span style="color: rgba(0, 128, 128, 1)">64</span> <span style="color: rgba(0, 0, 0, 1)">      }
</span><span style="color: rgba(0, 128, 128, 1)">65</span> <span style="color: rgba(0, 0, 0, 1)">   }
</span><span style="color: rgba(0, 128, 128, 1)">66</span>    unlock(&amp;<span style="color: rgba(0, 0, 0, 1)">allpLock)
</span><span style="color: rgba(0, 128, 128, 1)">67</span>    <span style="color: rgba(0, 0, 255, 1)">return</span><span style="color: rgba(0, 0, 0, 1)"> uint32(n)
</span><span style="color: rgba(0, 128, 128, 1)">68</span> }</pre>
</div>
</div>
<div>在进行抢占式调度,Go还涉及到利用操作系统信号方式来进行抢占,这里就不在介绍,感兴趣可以自己去研究。</div>
</div>
</div>
</div>
</div>
</div>
<div>&nbsp;</div>
<div>另外,本文图片没有一张原创,画图非常耗费时间,我没有那么多时间,所以只能到处借用大家的图片,侵权请联系。</div>
<h2 id="ygzO-1654346361100">6 参考资料</h2>
<div>
<ul>
<li>Go语言的调度模型:https://www.cnblogs.com/lvpengbo/p/13973906.html</li>
<li>深度揭秘Go语言 sync.Pool:https://www.cnblogs.com/qcrao-2018/p/12736031.html</li>
<li>https://toutiao.io/posts/2gic8x/preview</li>
<li>万字长文深入浅出go runtime:https://zhuanlan.zhihu.com/p/95056679</li>
<li>深入go runtime的调度:https://zboya.github.io/post/go_scheduler/</li>
<li>字节Go面试:https://leetcode-cn.com/circle/discuss/A0YstA/</li>
<li>golang调度学习-调度过程:https://blog.csdn.net/diaosssss/article/details/93066804</li>
<li>Go调度器中的三种结构:G、P、M:https://blog.csdn.net/chenxun_2010/article/details/103696394</li>
<li>Go语言的调度模型:https://www.cnblogs.com/lvpengbo/p/13973906.html</li>
<li>Go GMP的调度模型:https://zhuanlan.zhihu.com/p/413970952</li>
<li>详细剖析Go语言调度模型的设计:https://www.elecfans.com/d/1670400.html</li>
<li>Go的核心goroutine sysmon:https://blog.csdn.net/RA681t58CJxsgCkJ31/article/details/108633220</li>
<li>一文教你搞懂Go中栈操作:https://zhuanlan.zhihu.com/p/364813527</li>
<li>详解Go语言调度循环代码实现:https://www.luozhiyun.com/archives/448</li>
<li>goroutine的创建、休眠与恢复:https://zhuanlan.zhihu.com/p/386595432</li>
</ul>
</div>
<p>&nbsp;</p>
</div>
</div>
</div>
</div>

</div>
<div id="MySignature" role="contentinfo">
    <div style="font-size: 14px" id="green_channel">
您可以考虑给树发个小额微信红包以资鼓励
<img id="view_img" width="350" src="//images2017.cnblogs.com/blog/412020/201712/412020-20171216224224593-1205430224.png" alt="" border="0">
</div><br><br>
来源:https://www.cnblogs.com/dojo-lzz/p/16342622.html
頁: [1]
查看完整版本: GO GMP协程调度实现原理 5w字长文史上最全