GO线程调度——GMP

深入理解 Go 语言线程调度

• 进程:资源分配的基本单位 • 线程:调度的基本单位 • 无论是线程还是进程,在 linux 中都以 task_struct 描述,从内核角度看,与进程无本质区别 • Glibc 中的 pthread 库提供 NPTL(Native POSIX Threading Library)支持

 

 

 

进程切换开销

• 直接开销 • 切换页表全局目录(PGD) • 切换内核态堆栈 • 切换硬件上下文(进程恢复前,必须装入寄存器的数据统称为硬件上下文) • 刷新 TLB • 系统调度器的代码执行 • 间接开销 • CPU 缓存失效导致的进程需要到内存直接访问的 IO 操作变多

线程切换开销 • 线程本质上只是一批共享资源的进程,线程切换本质上依然需要内核进行进程切换 • 一组线程因为共享内存资源,因此一个进程的所有线程共享虚拟地址空间,线程切换相比进程 切换,主要节省了虚拟地址空间的切换

用户线程 无需内核帮助,应用程序在用户空间创建的可执行单元,创建销毁完全在用户态完成。

 

Goroutine

Go 语言基于 GMP 模型实现用户态线程 • G:表示 goroutine,每个 goroutine 都有自己的栈空间,定时器, 初始化的栈空间在 2k 左右,空间会随着需求增长。 • M:抽象化代表内核线程,记录内核线程栈信息,当 goroutine 调度 到线程时,使用该 goroutine 自己的栈信息。 • P:代表调度器,负责调度 goroutine,维护一个本地 goroutine 队 列,M 从 P 上获得 goroutine 并执行,同时还负责部分内存的管理

 

 

GMP 模型细节

 

G 所处的位置 • 进程都有一个全局的 G 队列 • 每个 P 拥有自己的本地执行队列 • 有不在运行队列中的 G • 处于 channel 阻塞态的 G 被放在 sudog • 脱离 P 绑定在 M 上的 G,如系统调用 • 为了复用,执行结束进入 P 的 gFree 列表中的 G

Goroutine 创建过程 • 获取或者创建新的 Goroutine 结构体 • 从处理器的 gFree 列表中查找空闲的 Goroutine • 如果不存在空闲的 Goroutine,会通过 runtime.malg 创建一个栈大小足够的新结构体 • 将函数传入的参数移到 Goroutine 的栈上 • 更新 Goroutine 调度相关的属性,更新状态为_Grunnable • 返回的 Goroutine 会存储到全局变量 allgs 中

将 Goroutine 放到运行队列上 • Goroutine 设置到处理器的 runnext 作为下一个处理器 执行的任务 • 当处理器的本地运行队列已经没有剩余空间时,就会把 本地队列中的一部分 Goroutine 和待加入的 Goroutine 通过 runtime.runqputslow 添加到调度器持有的全局 运行队列上

调度器行为(P) • 为了保证公平,当全局运行队列中有待执行的 Goroutine 时,通过 schedtick 保证有一定 几率会从全局的运行队列中查找对应的 Goroutine • 从处理器本地的运行队列中查找待执行的 Goroutine • 如果前两种方法都没有找到 Goroutine,会通过 runtime.findrunnable 进行阻塞地查找 Goroutine • 从本地运行队列、全局运行队列中查找 • 从网络轮询器中查找是否有 Goroutine 等待运行 • 通过 runtime.runqsteal 尝试从其他随机的处理器中窃取待运行的 Goroutine

如果想了解地更详细,这里有一篇通俗易懂的文章[典藏版]Golang调度器GPM原理与调度全分析 - 简书

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值