企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
## 调度器的生命周期 ![](https://img.kancloud.cn/54/34/543438a79d1e7cbcd8847a1858d3e200_439x872.png) 特殊的 M0 和 G0 M0 M0 是启动程序后的编号为 0 的主线程,这个 M 对应的实例会在全局变量 runtime.m0 中,不需要在 heap 上分配,M0 负责执行初始化操作和启动第一个 G, 在之后 M0 就和其他的 M 一样了。 G0 G0 是每次启动一个 M 都会第一个创建的 gourtine,G0 仅用于负责调度的 G,G0 不指向任何可执行的函数,每个 M 都会有一个自己的 G0。在调度或系统调用时会使用 G0 的栈空间,全局变量的 G0 是 M0 的 G0。 我们来跟踪一段代码 ~~~ package main import "fmt" func main() { fmt.Println("Hello world") } ~~~ 接下来我们来针对上面的代码对调度器里面的结构做一个分析。 也会经历如上图所示的过程: * 1.runtime 创建最初的线程 m0 和 goroutine g0,并把 2 者关联。 * 2.调度器初始化:初始化 m0、栈、垃圾回收,以及创建和初始化由 GOMAXPROCS 个 P 构成的 P 列表。 * 3.示例代码中的 main 函数是 main.main,runtime 中也有 1 个 main 函数 ——runtime.main,代码经过编译后,runtime.main 会调用 main.main,程序启动时会为 runtime.main 创建 goroutine,称它为 main goroutine 吧,然后把 main goroutine 加入到 P 的本地队列。 * 4.启动 m0,m0 已经绑定了 P,会从 P 的本地队列获取 G,获取到 main goroutine。 * 5.G 拥有栈,M 根据 G 中的栈信息和调度信息设置运行环境 * 6.M 运行 G * 7.G 退出,再次回到 M 获取可运行的 G,这样重复下去,直到 main.main 退出,runtime.main 执行 Defer 和 Panic 处理,或调用 runtime.exit 退出程序。 调度器的生命周期几乎占满了一个 Go 程序的一生,runtime.main 的 goroutine 执行之前都是为调度器做准备工作,runtime.main 的 goroutine 运行,才是调度器的真正开始,直到 runtime.main 结束而结束