深入 golang runtime 的调度

理解调度器的启动

runtime:

scheduler:

TLS:

spinning:

systemstack,mcall,asmcgocall

主要源码文件:

调度基本组件:

**G (goroutine)**:调度器的基本单位,存储的 goroutine 的执行 stack 信息,状态以及任务函数

在 g 的眼中只有 p,p 就是运行的 G 的 “CPU”

相当于两级线程

g 的任务函数

每个 g 的实例都有任务函数,如下代码

plaintext
1
2
userFun:=func(){fmt.Println("111")}
go userFunc();

go 的关键词创建了一个 goroutine, 此时 gouroutine 的任务函数 userFun

P(processor)

p 表示逻辑 processor,代表 M 执行的上下文

p 的最大作用是拥有各种 G 的对象队列,链表,cache, 和状态

p 的数量也代表 go 的执行并发度,即多少个 goroutine 可以同时执行

这里的 p 虽然表示逻辑处理器,但是 p 并不代表任何执行代码,对于 g 来说,p 相当于 cpu 的核,g 只有绑定 p 才能调度。对于 M 来说,p 提供了执行环境(Context),如分配内存状态(mcache), 任务队列 G 等

M(machine)

M 代表真正的执行计算资源,可以任务他就是 os thread (系统线程)

M 是真正的执行者,每个 M 就像一个勤劳的工作者,总是从各种队列找到可运行的 G, 而且这样的 M 的可以同时存在多个

M 在绑定有效 P,可以进行调度循环,而且 M 并不保留 G 状态,这个是 g 可以跨 M 调度的基础