sysmon
sysmon เป็นฟังก์ชันธรรมดา แปลตรงตัวว่าตัวตรวจสอบระบบ เมื่อลบส่วนความคิดเห็นออกแล้วจะมีโค้ดประมาณ 200 บรรทัด มันจะถูกกำหนดเธรดแยกต่างหากเพื่อเริ่มต้นในช่วงการนำทางโปรแกรม หลังจากนั้นจะทำงานในพื้นหลังอย่างต่อเนื่องเพื่อตรวจสอบสถานะขณะรันไทม์ของโปรแกรม Go และดำเนินการตอบสนองที่เหมาะสม สำหรับโค้ดส่วนการเริ่มต้นนี้สามารถดูได้ในฟังก์ชัน runtime.main:
func main() {
...
mp := getg().m
mainStarted = true
systemstack(func() {
newm(sysmon, nil, -1)
})
...
}ตัวตรวจสอบระบบเองเป็นเพียงลูป for หนึ่งรอบ ช่วงเวลาระหว่างรอบคือ 20 ไมโครวินาที เมื่อค่าดัชนีการว่างของโปรแกรมเพิ่มขึ้น ช่วงเวลาจะเพิ่มขึ้นสูงสุดเป็น 10 มิลลิวินาที ในแต่ละรอบของลูป มันจะทำสิ่งหลักๆ ต่อไปนี้:
- ช่วยการกำหนดการคอร์รูทีน ยึดการควบคุมคอร์รูทีนที่ทำงานเป็นเวลานาน
- ตรวจสอบสถานการณ์หน่วยความจำ และตัดสินว่าจำเป็นต้องทำ garbage collection หรือไม่
- คืนหน่วยความจำที่ไม่ได้ใช้ให้กับระบบปฏิบัติการ
- ปิดการเชื่อมต่อที่ไม่ได้ใช้เป็นเวลานานใน network poller
การยึดการควบคุม
ในส่วน นโยบายการกำหนดการ ได้กล่าวไว้ว่า ตัวตรวจสอบระบบเป็นหนึ่งในจุดเริ่มต้นของการยึดการควบคุมแบบอะซิงโครนัส เมื่อคอร์รูทีนทำงานเกิน 10 มิลลิวินาที ตัวตรวจสอบระบบจะบังคับให้เกิดการยึดการควบคุม งานส่วนนี้ทำโดยฟังก์ชัน runtime.retake ด้านล่างคือโค้ดที่ลดทอนแล้ว:
func retake(now int64) uint32 {
n := 0
lock(&allpLock)
for i := 0; i < len(allp); i++ {
pp := allp[i]
if pp == nil {
continue
}
pd := &pp.sysmontick
s := pp.status
sysretake := false
if s == _Prunning || s == _Psyscall {
// Preempt G if it's running for too long.
t := int64(pp.schedtick)
if int64(pd.schedtick) != t {
pd.schedtick = uint32(t)
pd.schedwhen = now
} else if pd.schedwhen+forcePreemptNS <= now {
preemptone(pp)
sysretake = true
}
}
}
unlock(&allpLock)
return uint32(n)
}Garbage Collection
ตัวตรวจสอบระบบจะตรวจสอบสถานการณ์หน่วยความจำอย่างสม่ำเสมอ เพื่อตัดสินว่าจำเป็นต้องทำ garbage collection หรือไม่ เมื่อเวลาผ่านไปเกิน 2 นาทีนับจาก GC ครั้งล่าสุด จะบังคับให้เกิด GC หนึ่งครั้ง
if lastgc != 0 && now-lastgc > forcegcperiod && atomic.Load(&forcegc.idle) != 0 {
lock(&forcegc.lock)
forcegc.idle = 1
var list gList
list.push(forcegc.g)
injectglist(&list)
unlock(&forcegc.lock)
}การคืนหน่วยความจำ
ตัวตรวจสอบระบบจะคืนหน่วยความจำที่ไม่ได้ใช้ให้กับระบบปฏิบัติการ งานส่วนนี้ทำโดยฟังก์ชัน runtime.scavenge
scavengeThreshold := scavengeRatio * float64(memstats.heap_inuse)
if scavengeThreshold > float64(scavengeMinPagesToRetain) {
scavenge(uintptr(scavengeThreshold), false)
}Network Poller
ตัวตรวจสอบระบบจะปิดการเชื่อมต่อที่ไม่ได้ใช้เป็นเวลานานใน network poller เพื่อหลีกเลี่ยงการสูญเสียทรัพยากร
if netpollinited() && sched.lastpoll != 0 {
if now-sched.lastpoll > 10*1e9 {
closeIdleNetpollConns()
}
}ช่วงเวลาลูป
ช่วงเวลาลูปของตัวตรวจสอบระบบจะปรับเปลี่ยนแบบไดนามิกตามระดับความว่างของโปรแกรม อย่างน้อยคือ 20 ไมโครวินาที อย่างมากคือ 10 มิลลิวินาที
delay := uint32(20) * uint32(sched.idle)
if delay > 10*1000 {
delay = 10 * 1000
}
usleep(delay)