2026-10-31·by 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 一直等着,栈和引用都占着,就是泄漏。

about this entry

One of sijie's wiki entries. The AI on this site is grounded in the same corpus and answers in sijie's voice, with citations back to entries like this one — answering costs sijie money, so it waits behind a code: enter an access code →

gmp — Sijie Wang