很多时候,当我们跟着源码去理解某种事物时,基本上可以认为是以时间顺序展开,这是编年体的逻辑。还有另一种逻辑,纪传体,它以人物为中心编排史事,使得读者更聚焦于某个人物。以一种新的视角,把所有的事情串连起来,令人大呼过瘾。今天我们试着以这样一种逻辑再看 g0。

回顾一下 Go 夜读第 78 期,关于调度器源码分析的内容。我们讲过,与主线程绑定的 M 对应的 g0 的主要作用是提供一个比一般 goroutine 要大的多栈(64K)供 runtime 代码执行。

初始化的过程中,在函数 runtime·rt0_go 里会给主线程的 g0 分配栈空间:

之后,主线程会与 m0 绑定,m0 又与 g0 绑定:

之后,又与 p0 绑定:

这样,主线程的这一套 GPM 就可以转起来了。接着,就创建了 main goroutine,放入 p0 的本地待运行队列。最后,通过 schedule() 函数进入调度循环。

前面说的是程序初始化的过程中,g0 是如何诞生的。当执行到 main.main() 函数,也说是用户在 main 包下写的 main 函数里,我们随手一句:

go func() {
// 要做的事
}()

就启动了一个 goroutine 时,在 Go 编译器的作用下,最终会转化成 newproc 函数。在 newproc 函数的内部,会在 g0 栈上调用 newproc1 函数,完成后续的工作。创建完成后,会将新创建的 goroutine 放入 _p_ 的本地待运行队列。

因为新增加了一个 g,这时会尝试去唤醒一个 P 来一起执行任务。判断条件是:

if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 && mainStarted {
wakep()
}

即在有空闲 P 以及没有正在“找工作的 M”的情况下,才会尝试去唤醒一个 P。我们又知道,其实 P 的数量在程序运行过程中一般不会变化,所以这里所谓的唤醒其实就是把空闲的 P 利用起来。

通过 wakep() -> startm() -> newm() -> allocm() -> malg() 这条链路创建 g0,这里 g0 的栈大小实际上为 8KB

mp.g0 = malg(8192 * sys.StackGuardMultiplier) // sys.StackGuardMultiplier 在 linux 里为 1

g0 作为一个特殊的 goroutine,为 scheduler 执行调度循环提供了场地(栈)。对于一个线程来说,g0 总是它第一个创建的 goroutine。之后,它会不断地寻找其他普通的 goroutine 来执行,直到进程退出。

当需要执行一些任务,且不想扩栈时,就可以用到 g0 了,因为 g0 的栈比较大。g0 其他的一些“职责”有:创建 goroutine、deferproc 函数里新建 _defer、垃圾回收相关的工作(例如 stw、扫描 goroutine 的执行栈、一些标识清扫的工作、栈增长)等等。

因为 g0 这样一个特殊的 goroutine 所做的工作,使得 Go 程序运行地更快。

注:最近在 medium 上看到了一个非常赞的关于 Go 的博客,题图画得很有阅读的欲望。这篇文章也是参考于其中的一篇

最新文章

  1. openswan-ipsec.conf配置说明
  2. 移动Web触控事件总结
  3. 屏蔽input导致的回车提交事件
  4. paip.信用卡账单处理分类bug o21
  5. JavaScriptSerializer中日期序列化问题解决方案
  6. Revit 二次开发 (二) 倾斜的板
  7. Codeforces Round #313 (Div. 2) C. Gerald's Hexagon
  8. JS单词形式的运算符
  9. 详解Android动画之Frame Animation
  10. C程序设计语言练习题1-17
  11. VLC源码分析知识总结
  12. 【CentOS如何最小化安装】
  13. php实现批量修改文件名称
  14. ruby轻松自删除代码
  15. python 中and,or计算规则
  16. 大杂烩 -- Iterator 并发修改异常ConcurrentModificationException
  17. 王之泰201771010131《面向对象程序设计(java)》第一周学习总结
  18. Visual Studio不显示智能提示代码,快捷键Alt+→也不出现
  19. java Runnable、Callable、FutureTask 和线程池
  20. HBase二级索引与Join

热门文章

  1. (数据科学学习手札100)搞定matplotlib中的字体设置
  2. spring入门学习
  3. 【面试专栏】JAVA锁机制
  4. Spring Boot 启动事件和监听器,太强大了!
  5. Kubernetes【K8S】(四):资源控制器
  6. 使用plsql 连接oracle数据库
  7. RocketMQ集群搭建(3m-3s-async)
  8. 设计模式——责任链(结合Tomcat中Filter机制)
  9. 怎样用Python自制好看的指数估值图
  10. 从零实现Linux一键自动化部署.netCore+Vue+Nginx项目到Docker中