Go 原理剖析(协程调度)

GMP 调度模型

G – Goroutine

  • 协程(用户级线程)
  • go func() 产生
  • 每个 G 在用户空间中都有独立的栈(2KB,可以动态扩容缩容)
  • G0:每次启动一个 M 创建的第一个 G,仅用于调度 G

M – Machine

  • 直接映射到操作系统内核线程
  • G 得以在 CPU 上执行的载体
  • M 的最大数量为 10000
  • M0:Go 程序启动后创建的第一个 M

P – Processor

  • 维护一个长度为 256 的 LRQ(Local Run Queue),可以通过 CAS 无锁访问,M 优先从自己关联的 P 的 LRQ 中获取 G,大大减少了从 GRQ(Global Run Queue)中获取 G 的锁竞争
  • P 的数量等于 GOMAXPROCS(一般设置为 CPU 核心数),限制了真正并行的 M 的数量

调度循环

系统调用

非阻塞式系统调用(chan 操作、网络 I/O)

当进行 chan 操作或网络 I/O 时,G 会和 MP 分离,被挂到 Netpoller 上,M 会进行下一次调度

当 chan 操作或网络 I/O 完成后,G 被唤醒,

1. 有自旋 M:G 被放入自旋 M 关联的 P 的 LRQ 中

2. 有空闲 P:唤醒一个休眠的 M 或创建一个新的 M 来关联空闲 P

3. 无空闲 P:G 被放入 GRQ 中

阻塞式系统调用(文件 I/O)

GM 会和 P 分离,G 和 M 一起进入挂起状态,如果 P 中仍有可运行的 G,会唤醒一个休眠的 M 或创建一个新的 M 来关联 P(Hand Off

当阻塞式系统调用完成,M 会尝试关联一个空闲的 P(优先关联原来的 P)以执行 G,如果没有空闲的 P,G 被放入 GRQ,M 休眠

抢占机制

系统线程 sysmon:独立于 Go 调度器运行,每 20us~10ms 启动一次,监控所有正在运行的 G,一旦发现某个 G 的运行时间超过了 10 ms 的阈值,sysmon 就会认为它需要被抢占

协作式抢占

sysmon 把“超时” G 标记为“可抢占”,“超时” G 会在抢占点(函数调用、系统调用、GC等)检查“可抢占”标记,主动让出 M

基于信号的异步抢占

sysmon 向“超时” G 所在 M 发送一个 SIGURG 信号,“超时” G 被抢占

暂无评论

发送评论 编辑评论


				
|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇