上卷 程序设计
中卷 标准库
- bufio 1.18
- bytes 1.18
- io 1.18
- container 1.18
- encoding 1.18
- crypto 1.18
- hash 1.18
- index 1.18
- sort 1.18
- context 1.18
- database 1.18
- connection
- query
- queryrow
- exec
- prepare
- transaction
- scan & null
- context
- tcp
- udp
- http
- server
- handler
- client
- h2、tls
- url
- rpc
- exec
- signal
- embed 1.18
- plugin 1.18
- reflect 1.18
- runtime 1.18
- KeepAlived
- ReadMemStats
- SetFinalizer
- Stack
- sync 1.18
- atomic
- mutex
- rwmutex
- waitgroup
- cond
- once
- map
- pool
- copycheck
- nocopy
- unsafe 1.18
- fmt 1.18
- log 1.18
- math 1.18
- time 1.18
- timer
下卷 运行时
源码剖析
附录
文章来源于网络收集而来,版权归原创者所有,如有侵权请及时联系!
4.7.2 系统监控
监控(sysmon)自运行时启动时,就使用专门的线程运行。
- 超过 2 分钟没有垃圾回收,强制执行。
- 超 10 毫秒未网络轮询(netpoll),执行。
- 向长时间运行 G 任务发出抢占调度。
- 收回闲置超过 10 毫秒的 syscall P。
- 检查定时器。
// proc.go // The main goroutine. func main() { if GOARCH != "wasm" { // no threads on wasm yet, so no sysmon systemstack(func() { newm(sysmon, nil, -1) }) } }
// forcegcperiod is the maximum time in nanoseconds between garbage // collections. If we go this long without a garbage collection, one // is forced to run. var forcegcperiod int64 = 2 * 60 * 1e9 // Always runs without a P, so write barriers are not allowed. func sysmon() { idle := 0 // 闲置(什么都没做)周期计数。 delay := uint32(0) // 循环间隔时间。 for { if idle == 0 { // 初始 20 微秒。 delay = 20 } else if idle > 50 { // 闲置超过 50 周期,加倍。 delay *= 2 } if delay > 10*1000 { // 最高 10 毫秒。 delay = 10 * 1000 } usleep(delay) now := nanotime() // STW,或没有 P 在工作,休眠。 if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) { if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) { syscallWake := false // 下个定时器触发时间。 next, _ := timeSleepUntil() // 无需处理定时器,休眠。 if next > now { // 休眠标志。 atomic.Store(&sched.sysmonwait, 1) // 休眠时间(短时间)。 sleep := forcegcperiod / 2 if next-now < sleep { sleep = next - now } // 休眠,等待唤醒或超时。 syscallWake = notetsleep(&sched.sysmonnote, sleep) atomic.Store(&sched.sysmonwait, 0) noteclear(&sched.sysmonnote) } // 被 syscall 等主动唤醒,重置闲置和间隔。 // 因为有新任务将执行。 if syscallWake { idle = 0 delay = 20 } } } lock(&sched.sysmonlock) now = nanotime() // 超过 10ms,轮询网络。 lastpoll := int64(atomic.Load64(&sched.lastpoll)) if netpollinited() && lastpoll != 0 && lastpoll+10*1000*1000 < now { // 更新最后轮询时间。 atomic.Cas64(&sched.lastpoll, uint64(lastpoll), uint64(now)) // 将轮询结果(Gs)加入全局队列。 list := netpoll(0) // non-blocking - returns list of goroutines if !list.empty() { injectglist(&list) } } // 唤醒异步物理内存释放 G。 if atomic.Load(&scavenge.sysmonWake) != 0 { wakeScavenger() } // 抢夺 syscall P,抢占调度长时间运行 G。 if retake(now) != 0 { idle = 0 } else { idle++ } // 距上次垃圾回收已超过 2 分钟,强制执行。 if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && atomic.Load(&forcegc.idle) != 0 { var list gList list.push(forcegc.g) injectglist(&list) } unlock(&sched.sysmonlock) } }
通过比对前次检查记录和当前计数器,来决定是否采取措施。
// runtime2.go, proc.go type p struct { schedtick uint32 // incremented on every scheduler call syscalltick uint32 // incremented on every system call sysmontick sysmontick // last tick observed by sysmon } type sysmontick struct { // 保存最后一次检查记录。 schedtick uint32 // schedule/execute 执行次数。 schedwhen int64 // schedule/execute 检查更新时间。 syscalltick uint32 // syscall 执行次数。 syscallwhen int64 // syscall 检查更新时间。 }
// proc.go // forcePreemptNS is the time slice given to a G before it is // preempted. const forcePreemptNS = 10 * 1000 * 1000 // 10ms
func retake(now int64) uint32 { n := 0 // 循环检查所有 P。 lock(&allpLock) for i := 0; i < len(allp); i++ { _p_ := allp[i] if _p_ == nil { // This can happen if procresize has grown // allp but not yet created new Ps. continue } // 上次检查记录。 pd := &_p_.sysmontick // P 状态。 s := _p_.status if s == _Prunning || s == _Psyscall { // 比较当前计数(p.schedtick)和上次记录(pd.schedtick)。 // 不等,表示有新任务被执行,更新检查记录。 // 相等,表示还在执行上次检查的任务。继续判断执行时间(两次检 // 查间隔)是否超过 10ms,以此决定是否发出抢占调度信号。 t := int64(_p_.schedtick) if int64(pd.schedtick) != t { pd.schedtick = uint32(t) pd.schedwhen = now } else if pd.schedwhen+forcePreemptNS <= now { preemptone(_p_) sysretake = true } } if s == _Psyscall { // 有新调用,更新检查记录。 t := int64(_p_.syscalltick) if !sysretake && int64(pd.syscalltick) != t { pd.syscalltick = uint32(t) pd.syscallwhen = now continue } // 跳过: P 没额外任务 + 有其他自旋或闲置 P + 检查间隔少于 10 ms。 // 上述任何一个条件不满足,都需要抢夺 P,去积极执行其他任务。 if runqempty(_p_) && atomic.Load(&sched.nmspinning)+atomic.Load(&sched.npidle) > 0 && pd.syscallwhen+10*1000*1000 > now { continue } unlock(&allpLock) // 修改状态,使得 syscallexit 无法继续使用该 P。(抢夺) if atomic.Cas(&_p_.status, s, _Pidle) { n++ _p_.syscalltick++ // 让 P 找个好人家,积极工作吧!奥利给! handoffp(_p_) } lock(&allpLock) } } unlock(&allpLock) return uint32(n) }
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论