2026-10-31·par Sijie Wang

gmp

GMP scheduling: Goroutine, Machine, Processor. M:N scheduling, work stealing, syscall handoff, netpoll, and who blocks whom.

GMP 调度:Goroutine、Machine、Processor

动机:又要任务便宜,又要真正并行。 一个操作系统线程要大栈和内核调度,一任务一线程撑不到十万并发。但任务如果只跑在一个线程上,又用不了多核。Go 于是用 M:N 调度:大量便宜的用户级任务(G)复用到少量操作系统线程(M)上,再用第三个东西(P)卡住真正并行的上限。

G = Goroutine。 go 语句创建的便宜用户级任务,自带栈(起步小、可增长)和自己的状态。创建便宜,不等于免费(见文末泄漏警告)。

M = Machine。 操作系统线程,是真正干活的工人。只有持有 P 时,M 才能跑 Go 代码。没有 P 的 M 什么都不跑。

P = Processor。 调度上下文,不是 CPU 核。它装着本地运行队列(容量 256)、一个叫 runnext 的优先槽(插队用)、以及内存缓存这类调度账。P 的个数就是 GOMAXPROCS,默认等于 CPU 核数。P 就是令牌:持有 P,才准跑 Go 代码。

调度循环,按顺序拿活。 持有 P 的 M 先拿 runnext(一个插队的优先槽),再拿本地队列队头,再从全局队列批量拿,最后随机挑一个受害 P 偷走它本地队列约一半的活。空闲的 P 会主动偷活,所以一个 P 排长队、旁边一个 P 空闲的局面不会站住。

本地队列满了是批量溢出,不是扩容。 本地队列满到 256 时,一次把一半移到全局队列。没有按 P 扩容这回事。

阻塞式系统调用会交出 P。 M 进入阻塞式系统调用时释放 P(entersyscall),空闲的 M 或新创建的 M 捡起这个 P 继续跑别的 goroutine。调用返回时,M 试着把原来的 P 拿回来(快路径);拿不到,它的 goroutine 就到全局队列上等着,M 自己休眠。P 从来不会被一个堵住的线程卡住。

异步抢占防止一个 goroutine 饿死别的。 sysmon 线程盯着,跑了约 10 毫秒还不让出的 goroutine 会被抢占(Go 1.14 起,信号机制),死循环占不住 P。

网络 I/O 不占 M。 网络等待走 netpoll(epoll/kqueue):socket 交给 poller,goroutine 挂起等事件,M 继续跑别的 goroutine。这就是 NIO 的形状,跟 Netty NIO 一个意思。

谁堵着谁,最容易混的一节。 三种阻塞,三个不同答案。运行时挂起(channel 收发、sleep、互斥锁):goroutine 等着,M 继续跑,因为 runtime 是把 goroutine 自己摘下来。网络等待:goroutine 等 netpoll 事件,M 继续跑。阻塞式系统调用(文件 I/O、部分 cgo 调用):M 真被内核堵住了,于是交出 P,另一个 M 跑别的 goroutine。Go 代码里堵住,不等于线程堵住;只有第三种堵 M,而且连 P 都不堵。

调优旋钮。 GOMAXPROCS 定 P 的个数;容器里应该跟 cgroup 的 CPU 配额走(automaxprocs 干这个),而不是宿主机核数。M 的个数按需长,上限 10,000(debug.SetMaxThreads);空闲的 M 自旋一会儿找活,找不到就放下 P 休眠。

GC 和 P。 全停顿(STW)会停住所有 P。并发标记由 GC-worker goroutine 做,普通 goroutine 通过 GC assist 帮忙标记,标记活大家分,不留给专职队伍。

一个类比,加两个区别。 GMP 像“会互相偷活的连接池”:有限的执行槽位被复用,任务不独占槽位一生。区别一:goroutine 可以被抢占、可以在 P 和 M 之间迁移,不像连接池里一个请求全程占一个连接。区别二:work stealing 让空闲的 P 主动拿活,不存在一个 P 空闲、一个 P 排长队的局面。

三个坑,纠正版。 一:P 不是 CPU 核,是 GOMAXPROCS 定的调度上下文。二:M 不是 P;M 数可以比 P 多(堵住的 M 不持有 P),也可以比 P 少(空闲的 P 就空着);不变的是真正跑 Go 代码的 M 数永远不超过 P。三:不是所有阻塞都占 M,见上面谁堵着谁那张图。再补两个警告:本地队列满了是批量溢出不是扩容;goroutine 便宜不是免费,忘掉的 goroutine 一直等着,栈和引用都占着,就是泄漏。

à propos de cette entrée

L'une des entrées de wiki de sijie. L'IA sur ce site est fondée sur le même corpus et répond à la voix de sijie, avec citations revenant à des entrées comme celle-ci — répondre coûte argent à sijie, donc cela attend derrière un code : entrer un code d'accès →

gmp — Sijie Wang