golang GMP模型

  • 概述

  • G — 表示 Goroutine,它是一个待执行的任务;

  • M — 表示操作系统线程,它由操作系统的调度器调度和管理,实际承载goroutine的运行;

  • P — 表示逻辑处理器,它可以被看做运行在线程上的本地调度器。

  • M:N模型

Runtime 会在程序启动的时候,创建 M 个线程(CPU 执行调度的单位),之后创建的 N 个 goroutine 都会依附在这 M 个线程上执行。

  • scheduler调度

  1. P先从自己的本地runnable queue找一个G
  2. 要是没找到,就去别的P上找
  3. 没找到就去Global runnable queue找
  4. 还没找到就要去network poller中找阻塞在网络请求中的G
    此外, 每61次会强行去 Global runnable queue 找 G
  • GMP运行的简单流程

  1. Goroutine 创建:当使用 go 关键字创建一个新的 Goroutine 时,它会被放入 P 的本地队列若本地队列已满会放到调度器的全局队列中。
  2. 调度器调度:Go 的调度器会将 Goroutine 分配给一个 P,P 会尝试在其本地队列中执行 Goroutine。如果本地队列已满或长时间未执行,Goroutine 会被放入全局队列。
  3. M 的执行:M 会从 P 的本地队列或全局队列中取出 Goroutine 并执行。当 M 空闲时,它会尝试从全局队列或其他 P 的本地队列中 “窃取” Goroutine 来执行,这种机制称为 “work stealing”。
  • 如何保证执行死循环的 Goroutine 不会一直抢占M

  1. 系统信号: 调度器会周期性地向运行 Goroutine 的 M 线程发送一个系统信号(SIGURG

    栈扫描: 当 M 线程接收到这个信号后,会异步地暂停正在执行的 Goroutine。

    栈检查: 调度器会检查这个 Goroutine 的栈,寻找一个“安全点”。所谓安全点,就是指 Goroutine 的执行状态是可中断的。

    抢占: 如果找到了安全点,调度器会修改 Goroutine 的栈,将其标记为可抢占,并将其放入全局队列或本地队列。

    切换: M 线程随后就会去执行其他 Goroutine。

  2. 在发生系统调用、阻塞操作时,goroutine会被挂起,切换到其他goroutine


golang GMP模型
https://fatwang1.github.io/2024/12/20/2024121900/
作者
衣云乘风
发布于
2024年12月19日
许可协议