以下为本人阅读goroutine调度源码随手记的笔记,现在还是一个个知识点的形式,暂时还没整理,先发到这里,一点点更新:
 
1). runq [256]guintptr P 的runable队列最大只能保存256个G
 
2). 全局的可执行队列由schedt持有,runq gQueue
 
3). goroutine 状态从_Grunning转为_Grunnable后,解除g与m的关联,将g放入全局的任务队列,然后再找到一个新的_Grunable状态的G来执行
 func goschedImpl(gp *g) {
status := readgstatus(gp)
if status&^_Gscan != _Grunning {
dumpgstatus(gp)
throw("bad g status")
}
casgstatus(gp, _Grunning, _Grunnable)
dropg()
lock(&sched.lock)
globrunqput(gp)
unlock(&sched.lock) schedule()
}
4). 寻找下一个g来执行的函数 findrunnable 在proc.go:2178
大致分为三大步骤,一是尝试从本地队列获取一个,二是尝试从全局队列获取n个,三是尝试窃取。这里要说一下的是:从本地队列获取时和从全局队列获取时关于并发访问的控制是不一样的,从本地队列获取的时候,都是通过原子操作来和自旋锁来实现的,而从全局队列获取时,则是通过获取sched.lock锁控制的,这是一个全局的排他锁。
首先尝试从本地队列获取(本地队列先看runnext是否不为0,如果是,先返回runnext指向的g,否则从队首弹一个g出来),如果本地队列没有则从全局队列获取,如果全局队列也没有,就尝试从其他P的队列中窃取,但是窃取之前会先做如下几个判断:
1. sched.npidle == gomaxprocs - 1 也就是看其他的P是不是都空闲着,如果是的话,就没必要去窃取了。
2. 2*sched.nmspinning >= gomaxprocs - npidle 当前处于spinning状态的M的两倍 大于等于非空闲的P的数量. 翻译过来就是在正在窃取G的M的数量应该不超过非空闲状态的P的一半
3. 是否准备执行GC,如果是,就先不窃取了
 
 
5).从全局队列取一部分g到本地队列的操作。
源码位置: proc.go:4665 globrunqget (go1.12)
关于取多少个的问题。假设全局队列一共有t个G,一共有n个P,则,取的数量不超过t / n,并且不超过本队队列总长度的一半,目前1.14版本中,本地队列总长度为256,也就是每次从全局队列取的G的数量不超过128个.
 
 
6).关于选取哪个P进行窃取以及窃取多少个的问题:
源码位置:proc.go:2238 findrunnable(go1.12)
1. 选取窃取的P
首先一共进行4轮尝试,每轮都会遍历所有的P。
另外这里的遍历P的过程值得说一下,并不是从下标0位置一个个往后遍历的,而且跳跃式的遍历,而且跳跃的步长是随机出来的。关于步长的选取,算法是这样的:
首先定义了一个randomOrder结构如下:
type randomOrder struct {
count uint32 // P的数量
coprimes []uint32 // 小于等于count,且与count互质的数(从小到大排列)
}
首先会生成一个与P的数量互质的数的列表,然后随机出一个数i,遍历P列表的步长= i % len(coprimes)。开始遍历的位置=i % nprocs (nproc即P的数量,源码中是用i对count取模,而count的值是通过randomOrder.reset操作把nprocs赋值给count的).
有意思的是,当第一轮遍历完了之后,发现一个g都没拿到,这个时候就会把stealRunNextG的值置为true,这个值表示是否从别的P的runnext窃取,第一轮窃取的时候这个值都为false。(也就是饥不择食了)
 
7). 窃取数量
源码位置: runqgrab proc.go:4858
假设现在P1从P2窃取一部分G。关于窃取多少个G是怎么定的呢?有以下几点原则,第一,假设P2的可执行队列中G的数量为x个,窃取个数n = x - x / 2;第二不得超过P1的可执行队列(环形buffer)容量的一半,当前最新版本(go1.14)中P的可执行队列最大容量为256,因此,窃取G的个数不超过128个。
 
8). 最终没有找到可执行的G之后发生了什么
1. 断开m和p之间的联系
2. 把P放进sched.pidle(空闲P的链表)
3. 把M的spinning(是否正在获取可用G)状态改为false
4. 再次确认一遍所有的P的runq都是空的
5. 尝试轮训阻塞事件列表,看看是否有一些G已经转变为runable状态.此处获取到的gList将尝试空闲的P列表中取一个P出来,然后把gList放进P的runq。而如果没有空闲的P,那么将把gList放进全局的runq。
6. 若步骤5仍然没能获取到可执行的G,或者步骤5获取到了G,但是没有空闲的P,那么将这部分G放到全局队列后,M将停止运行,将当前M加入到空闲M列表,然后开始睡眠,直到下一次被唤醒。
 
 
9).全局队列的来源有以下几种:
1. 轮询等待队列,解除等待之后将放入全局队列
2. 在M上正常执行的g,被切换之后被放入全局队列
3. 插入新的goroutine时,如果本地队列已满,则会将本地队列的一半取出来,再加上新加入的g,一起放入全局队列,放入全局队列前,会将取出来的这个列表随机打乱顺序再放入proc.go:4803(go1.12)
 
 
10).一个go程序在64位机上,最小的栈空间大小是2048个字节,最大的栈空间大小是1G,在32位机上是250M,不过这里要注意,这里的1GB并不等于1024M,而是10的9次方字节,proc.go中有这样一句话:Using decimal instead of binary GB and MB because they look nicer in the stack overflow failure message.因为看起来更好看。
 
11).go关键字新建一个协程时,传的参数大小是有限制的,最大是2048个字节,实际上比这个还要小一点(经测试,参数最大为2000是没问题的,2001个字节的时候,会抛出这个错误:newproc: function arguments too large for new goroutine.
 
12).新建G时,不会总是重新分配,而是先尝试从本地空闲的G 列表里面取一个,如果本地空闲G列表没有,则从全局空闲G列表中取,如果都没取到,就重新分配一个。
 
13).关于新创建的G会放到哪个P的队列的问题
以下是runqput函数的注释,在创建新的G之后,传入的next始终是true的,所以,新创建的G会放到runnext,因此,新创建的G会在当前的G被换出后,立即执行,不用排队。
// runqput tries to put g on the local runnable queue.
// If next is false, runqput adds g to the tail of the runnable queue.
// If next is true, runqput puts g in the _p_.runnext slot.
// If the run queue is full, runnext puts g on the global queue.
 
 
14). sudog用来保存g的等待队列,例如channel的发送或接收等待队列
type sudog struct {
g *g
 
isSelect bool
next *sudog
prev *sudog
elem unsafe.Pointer // data element (may point to stack)
 
acquiretime int64
releasetime int64
ticket uint32
parent *sudog // semaRoot binary tree
waitlink *sudog // g.waiting list or semaRoot
waittail *sudog // semaRoot
c *hchan // channel
}

goroutine调度源码阅读笔记的更多相关文章

  1. mxnet源码阅读笔记之include

    写在前面 mxnet代码的规范性比Caffe2要好,看起来核心代码量也小很多,但由于对dmlc其它库的依赖太强,代码的独立性并不好.依赖的第三方库包括: cub dlpack dmlc-core go ...

  2. CI框架源码阅读笔记5 基准测试 BenchMark.php

    上一篇博客(CI框架源码阅读笔记4 引导文件CodeIgniter.php)中,我们已经看到:CI中核心流程的核心功能都是由不同的组件来完成的.这些组件类似于一个一个单独的模块,不同的模块完成不同的功 ...

  3. CI框架源码阅读笔记4 引导文件CodeIgniter.php

    到了这里,终于进入CI框架的核心了.既然是“引导”文件,那么就是对用户的请求.参数等做相应的导向,让用户请求和数据流按照正确的线路各就各位.例如,用户的请求url: http://you.host.c ...

  4. CI框架源码阅读笔记3 全局函数Common.php

    从本篇开始,将深入CI框架的内部,一步步去探索这个框架的实现.结构和设计. Common.php文件定义了一系列的全局函数(一般来说,全局函数具有最高的加载优先权,因此大多数的框架中BootStrap ...

  5. CI框架源码阅读笔记2 一切的入口 index.php

    上一节(CI框架源码阅读笔记1 - 环境准备.基本术语和框架流程)中,我们提到了CI框架的基本流程,这里再次贴出流程图,以备参考: 作为CI框架的入口文件,源码阅读,自然由此开始.在源码阅读的过程中, ...

  6. 源码阅读笔记 - 1 MSVC2015中的std::sort

    大约寒假开始的时候我就已经把std::sort的源码阅读完毕并理解其中的做法了,到了寒假结尾,姑且把它写出来 这是我的第一篇源码阅读笔记,以后会发更多的,包括算法和库实现,源码会按照我自己的代码风格格 ...

  7. Three.js源码阅读笔记-5

    Core::Ray 该类用来表示空间中的“射线”,主要用来进行碰撞检测. THREE.Ray = function ( origin, direction ) { this.origin = ( or ...

  8. PHP源码阅读笔记一(explode和implode函数分析)

    PHP源码阅读笔记一一.explode和implode函数array explode ( string separator, string string [, int limit] )此函数返回由字符 ...

  9. AQS源码阅读笔记(一)

    AQS源码阅读笔记 先看下这个类张非常重要的一个静态内部类Node.如下: static final class Node { //表示当前节点以共享模式等待锁 static final Node S ...

随机推荐

  1. Nginx 从入门到放弃(五)

    nginx的rewrite重写 nginx具有将一个路由经过加工变形成另外一个路由的功能,这就叫做重写. 重写中用到的指令 if (条件) {} 设定条件,再进行重写 set # 设定变量 retur ...

  2. 什么是X-UA-Compatible?X-UA-Compatible的作用

    代码 <meta http-eqiv="X-UA-Compatible" content="ie=edge"> 什么是X-UA-Compatible ...

  3. MySQL实验 子查询优化双参数limit

    MySQL实验 子查询优化双参数limit 没想到双参数limit还有优化的余地,为了亲眼见到,今天来亲自实验一下.   实验准备 使用MySQL官方的大数据库employees进行实验,导入该示例库 ...

  4. 【题解】p1809 过河问题

    原题传送门 题目分析 现有n个人在东岸,要过河去西岸.开始东岸有一艘船,船最多可承载2人,过河时间以耗时最长的人所需时间为准. 给定n个人的过河时间a,求所有人从东岸到西岸所需的最短时间. 当\(n= ...

  5. 解决vue项目中使用ivew定制主题报 .bezierEasingMixin();错误

    背景:在使用view-design(iview)定制主体时(覆盖变量方式)出现less错误 完整错误如下 解决方法: 在vue.config.js中添加 less-loader:5.0.x modul ...

  6. vue组件通讯之provide / inject

    什么是 provide / inject [传送门] vue的组件通讯方式我们熟知的有 props $emit bus vuex ,另外就是 provide/inject provide/inject ...

  7. JavaWeb项目的目录结构解释(上):

    当我们在IDEA创建JavaWeb项目时,默认的一般会有下图的目录结构,你的开发就是按照下列结构进行开发的,那么我就逐一解释他们的意思: 首先是demo:这个是项目的名字,一般你是自己创建一个与自己项 ...

  8. js自定义方法绑定元素事件

    //事件绑定封装 function addEvent(elem, type, handle){ if(elem.addEventListener){ elem.addEventListener(typ ...

  9. 小白从零开始阿里云部署react项目+node服务接口(二:node服务+web)

    我们用极简的方式来创建服务,没有任何附加功能 1 新建一个server文件夹 2 使用npm init 或者yarn init  一路enter 3  yarn add  express cors  ...

  10. Python Ethical Hacking - The Lab and Needed Software

    The Lab and Needed Software Attacker Machine - Kali Linux https://www.kali.org/ 1. Install the softw ...