Go的核心goroutine sysmon
原文作者Vincent Blanchon, 译者fliter 地址Go: sysmon, Runtime Monitoring
插图来自A Journey With Go
,由 Renee French方面提供
本篇文章基于Go 1.14
Go 的标准库提供了一种监测应用程序的线程,并帮你 (找寻) 程序可能遇到的瓶颈. 该线程称为sysmon
,即系统监视器 (system monitor).在GMP 模型中,这个 (特殊) 线程未链接任何的 P, 这意味着调度器 (scheduler) 没有将其考虑在内, 因此始终处于运行状态.
如下是带有此特殊线程的图:
更多关于GMP模型
的内容,推荐阅读作者的另一篇文章 协程,系统线程及 CPU 管理
同样, 通过Go tool trace无法追踪到此线程.
Scope
sysmon
线程的作用很广, 主要涉及以下方面:
由应用程序创建的计时器 (timers).
sysmon
线程查看应该在运行却仍在等待执行时间的计时器. 在这种情况下, Go 将查看空闲的 M 和 P 列表, 以便尽可能快地运行它们.网络轮询器和系统调用. 它将运行在网络操作中被阻塞的 goroutine.
垃圾回收器(如果已经很长时间没有运行). 如果垃圾回收器已经两分钟没有运行,则 sysmon 将强制执行一轮垃圾回收 (GC). 如下是用Go tool trace工具生成的追踪示例:
长时间运行的 goroutine 的抢占. 任何运行时间超过10 毫秒的 goroutine 都会被抢占, 将运行时间 (running time) 留给其他 goroutine.
有关异步抢占的更多信息,推荐阅读作者 Go:异步抢占
Pace
sysmon
足够聪明, 在无事可做时不会消耗资源. 其周期 (循环时间) 是动态的,取决于正在运行的程序的当前活动.
初始速度 (执行频次) 设置为20 纳秒,这意味着sysmon
线程一直在寻求 (哪里需要) 帮助. 然后,经过几个周期, 如果sysmon
线程没有执行任何操作, 则两个周期之间的休眠将加倍, 直至达到10 毫秒. 如果应用程序没有很多系统调用或 长时间运行的 goroutine, 则该线程将在大多数情形下,回退变为10 毫秒的延迟 (执行频次),从而给应用程序带来非常小的开销.
sysmon
线程还能够检测其何时不应运行, 如以下两种情况:
垃圾回收器即将要运行. (
sysmon
线程将在垃圾回收结束时恢复)所有线程都处于空闲状态,没有任何一个在运行中
在这两种情况下, sysmon
都会休眠,从而不会有任何不必要的资源消耗.
( 译者注:Go 并发的最小逻辑单位叫做 goroutine, 是 Go 为实现并发提供的用户态线程
,这种用户态线程运行在内核态线程(OS线程)之上
,也称为协程. 协程是一种用户态的轻量级线程, 其调度完全由用户控制. 从技术角度说,“协程就是你可以暂停执行的函数”. 协程拥有自己的寄存器上下文和栈. 协程调度切换时, 将寄存器上下文和栈保存到其他地方, 在切回来时,恢复先前保存的寄存器上下文和栈. 直接操作栈则基本没有内核切换的开销, 可以不加锁的访问全局变量,所以上下文的切换非常快.
Go 中的协程有三种:一种是主 (轻量级) 线程,一种是用来跑 sysmon 的 (轻量级) 线程,一种是普通的 (轻量级) 线程,
想这样一个问题:
在调度过程中,如果一个 goroutine 一直占有 CPU 又不会有阻塞或则主动让出 CPU 的调度,scheduler 怎么做抢占式调度让出 CPU?
有一个特殊的 sysmon 线程做抢占式调度, 当一个 goroutine 占用 CPU 超过10 毫秒之后,调度器会根据实际情况提供不保证的协程切换
这便是 sysmon 的作用之一 )
sysmon
有关的源码, 主要在runtime/proc.go
和runtime/runtime2.go
中