sysmon(System Monitor)是 Go 运行时系统中的一个后台监控 goroutine,负责系统级别的监控和管理任务。它在程序启动时创建,独立于用户 goroutine 运行,持续监控和优化运行时系统的状态。
设计目标
系统监控:监控 goroutine 和 P 的状态
性能优化:检测和回收长时间运行的 goroutine
资源管理:触发 GC、网络轮询等系统任务
异常检测:检测死锁、长时间阻塞等问题
sysmon 的特点
独立运行:在独立的 goroutine 中运行
后台执行:不影响用户 goroutine 的执行
周期性检查:定期执行各种监控任务
自适应间隔:根据系统负载调整检查间隔
sysmon 的主要功能
功能概览
graph TB
A[sysmon系统监控] --> B[retake 抢占]
A --> C[forcegc 强制GC]
A --> D[netpoll 网络轮询]
A --> E[scavenge 内存清理]
A --> F[死锁检测]
B --> B1[检测长时间运行的G]
B --> B2[检测长时间运行的P]
B --> B3[执行抢占]
C --> C1[检查GC条件]
C --> C2[触发GC]
D --> D1[轮询网络IO]
D --> D2[唤醒阻塞的G]
E --> E1[清理未使用的内存]
E --> E2[归还给操作系统]
style A fill:#ffcccc
style B fill:#ccffcc
style C fill:#ccccff
style D fill:#ffffcc
style E fill:#ffccff
style F fill:#ccccff
sequenceDiagram
participant sysmon as sysmon
participant P as P处理器
participant G as goroutine
sysmon->>P: 检查P状态
P-->>sysmon: 返回状态信息
alt P长时间运行
sysmon->>P: 设置抢占标志
P->>G: 检查抢占标志
G->>G: 让出执行权
end
alt G长时间运行
sysmon->>G: 设置抢占标志
G->>G: 检查抢占标志
G->>G: 让出执行权
end
// runtime/proc.go (简化) funcretake(now int64)uintptr { n := 0 // 遍历所有 P for i := 0; i < len(allp); i++ { _p_ := allp[i] if _p_ == nil { continue } pd := &_p_.syscalltick s := _p_.status // 检查 P 是否在系统调用中 if s == _Psyscall { // P 在系统调用中超过 20ms,需要抢占 if now-pd.syscallwhen > 20*1000*1000 { // 抢占 P if atomic.Cas(&_p_.status, s, _Pidle) { // 将 P 交给其他 M handoffp(_p_) } } } // 检查 G 是否长时间运行 if s == _Prunning { t := int64(_p_.schedtick) ifint64(pd.schedtick) != t { pd.schedtick = uint32(t) pd.schedwhen = now continue } // G 运行超过 10ms,需要抢占 if now-pd.schedwhen > 10*1000*1000 { // 抢占 G preemptone(_p_) } } } returnuintptr(n) }
抢占标志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
// runtime/proc.go (简化) funcpreemptone(_p_ *p)bool { mp := _p_.m.ptr() if mp == nil || mp == getg().m { returnfalse } gp := mp.curg if gp == nil || gp == mp.g0 { returnfalse } // 设置抢占标志 gp.preempt = true gp.stackguard0 = stackPreempt returntrue }
2. forcegc - 强制 GC
forcegc 负责在必要时强制触发垃圾回收。
工作原理
flowchart TD
A[sysmon检查] --> B{距离上次GC时间?}
B -->|>2分钟| C[检查堆内存]
B -->|<=2分钟| D[跳过]
C --> E{堆内存增长?}
E -->|是| F[触发GC]
E -->|否| D
F --> G[执行GC]
style F fill:#ffcccc
style G fill:#ccffcc
sequenceDiagram
participant sysmon as sysmon
participant netpoll as netpoll
participant G as 阻塞的goroutine
sysmon->>netpoll: 轮询网络事件
netpoll->>netpoll: 检查IO就绪
netpoll-->>sysmon: 返回就绪的goroutine列表
loop 每个就绪的goroutine
sysmon->>G: 唤醒goroutine
G->>G: 继续执行
end
网络轮询的作用
唤醒阻塞的 G:网络 IO 就绪时唤醒等待的 goroutine
提高并发:及时处理网络事件
减少延迟:快速响应网络事件
代码示例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
// runtime/proc.go (简化) funcsysmon() { for { // 网络轮询 if netpollinited() { list := netpoll(0) // 非阻塞轮询 if !list.empty() { // 将就绪的 goroutine 加入运行队列 injectglist(&list) } } // 其他监控任务... usleep(delay) } }
4. scavenge - 内存清理
scavenge 负责清理未使用的内存,将其归还给操作系统。
工作原理
flowchart TD
A[sysmon检查] --> B{内存使用率低?}
B -->|是| C[扫描内存页]
B -->|否| D[跳过]
C --> E{找到未使用页?}
E -->|是| F[标记为可清理]
E -->|否| D
F --> G[归还给操作系统]
style F fill:#ffcccc
style G fill:#ccffcc
// runtime/proc.go (简化) funcsysmon() { for { // 检查死锁 if checkdead() { // 检测到死锁,输出错误信息 throw("all goroutines are asleep - deadlock!") } // 其他监控任务... usleep(delay) } }
funccheckdead()bool { // 检查所有 P 是否都空闲 // 检查所有 G 是否都阻塞 // 如果满足死锁条件,返回 true return allPIdle() && allGBlocked() }
sysmon 的工作流程
主循环
flowchart TD
A[sysmon启动] --> B[初始化]
B --> C[进入主循环]
C --> D[计算检查间隔]
D --> E[执行retake抢占]
E --> F[检查forcegc]
F --> G[执行netpoll]
G --> H[执行scavenge]
H --> I[检查死锁]
I --> J[休眠]
J --> C
style A fill:#ffcccc
style C fill:#ccffcc
style J fill:#ccccff