sysmon是go runtime中独立于gmp调度器的常驻监控协程,不绑定p、不参与goroutine调度,通过newm创建专属m在系统栈运行无限循环,负责抢占阻塞p、驱动netpoll、触发gc及协作式抢占长时间运行g。

sysmon 不是调度器的一部分,也不参与 Goroutine 调度;它是个独立运行、不绑定 P 的后台监控协程,靠轮询+干预兜底系统健康——你没法在 go tool trace 里看到它,也别指望它帮你跑业务逻辑。
sysmon 怎么启动:不走 GMP 调度路径的特殊 M
它在 runtime.main 初始化完成后、用户 main.main 执行前,由主协程调用 systemstack(func() { newm(sysmon, nil, -1) }) 启动:
-
newm创建的是一个完全不关联任何 P 的 M,直接在系统栈上运行sysmon函数 -
sysmon内部是永不返回的for循环,靠usleep(delay)控制节奏,不依赖 Go 的调度器唤醒机制 - 因此它不会出现在
go tool trace中,也不会被pprof的 goroutine profile 捕获
sysmon 的轮询节奏:空闲时懒,忙时勤
初始休眠为 20μs,但会动态调整:
初始化并管理阿里云SDK Go 客户端,涵盖 sync.Once 单例、goroutine 安全、endpoint 与 region 配置、VPC 端点及调试。
- 连续 50 轮没做任何事(
idle > 50),delay指数增长 - 上限固定为 10ms(即
delay > 10*1000时截断) - 当
sched.gcwaiting == 1或所有 P 都空闲(npidle == gomaxprocs)时,它会跳过本轮循环,甚至进入更长休眠 - 若刚从系统调用中抢回一个 P(比如
retake成功),会重置idle = 0,立刻切回 20μs 频率
sysmon 做什么:四类关键干预,全属 runtime 级兜底
它只处理调度器自身无法及时响应的异常场景:
-
回收卡在
_Psyscall的 P:调用retake(now)检查超时(默认阈值约 20μs),强制解绑阻塞 M 并尝试绑定空闲 M,防止单个慢系统调用拖垮整个 P 资源池 -
驱动 netpoll:每轮调用
netpoll(0)(非阻塞轮询),拿到就绪 fd 后通过injectglist(&list)把对应 G 注入某个 P 的本地队列,不是唤醒原 M -
触发 GC:检查
forcegcperiod(默认 2 分钟)或内存分配速率,满足条件则调用forcegc();GC 运行期间它主动让路 -
抢占长时间运行 G:检测
schedtick计数器,对运行超 10ms 的 G 插入stackPreempt标记,下一次函数调用时触发协作式抢占
容易忽略的关键点:它不唤醒 M,也不处理 syscall 本身
常见误解是把 sysmon 当成“M 的闹钟”:
- 它对空闲 M(
notesleep(&m.park))可能调用notewakeup,但真正唤醒靠底层futex返回;对执行阻塞系统调用的 M,它完全不插手——那是entersyscall/exitsyscall和 netpoller 的事 - 它不处理普通网络读写逻辑,只负责轮询 netpoller 并注入 G;也不管理用户 timer,只协助运行计时器触发逻辑(比如发现 timer 到期但无空闲 P,会尝试唤醒 M)
- 它检查死锁(
checkdead())仅作为兜底:当无运行中 G、无等待 timer、且所有 P 空闲时才报警,但不会修复死锁
golang免费学习笔记(深入):立即使用
在学习笔记中,你将探索golang的核心概念和高级技巧!










