sysmon
sysmon adalah fungsi biasa, secara harfiah berarti system monitor, setelah menghapus bagian komentar hanya sekitar 200 baris kode. Ia akan dialokasikan thread terpisah saat tahap bootstrap program untuk启动, kemudian akan terus memonitor status program Go saat runtime di background dan mengambil tindakan yang sesuai. Bagian kode tentang启动nya dapat dilihat di fungsi runtime.main:
func main() {
...
mp := getg().m
mainStarted = true
systemstack(func() {
newm(sysmon, nil, -1)
})
...
}System monitor sendiri hanya sebuah for loop, interval setiap putaran loop adalah 20 mikrodetik, seiring dengan kenaikan idle index program, interval waktu maksimal akan meningkat hingga 10 milidetik. Di setiap putaran loop, ia terutama melakukan beberapa hal berikut:
- Assist scheduling goroutine, preempt goroutine yang berjalan dalam waktu lama
- Memeriksa kondisi memori dan menentukan apakah perlu melakukan garbage collection
- Memeriksa status network poller dan menangani event network yang timeout
- Memeriksa dan membersihkan cache memori yang tidak digunakan
Assist Scheduling
Scheduling assist adalah salah satu tugas penting sysmon, terutama mencakup dua aspek:
Preempt Goroutine
Ketika sebuah goroutine berjalan terlalu lama tanpa melakukan operasi yang dapat di-preempt (seperti alokasi memori, operasi channel, dll), sysmon akan mendeteksi dan memaksanya untuk di-preempt.
// Check for locked threads
// Preempt G if it's running too long
if now-gp.preemptAt > 10*1000*1000 { // 10ms
gp.preempt = true
gp.stackGuard0 = stackPreempt
}Mekanisme ini mencegah sebuah goroutine memonopoli CPU terlalu lama, memastikan fairness scheduling.
Check Network Poller
Sysmon juga bertanggung jawab untuk memeriksa status network poller, menangani event network yang timeout.
// Check network poller
if netpollinited() {
list, delta := netpoll(0)
if delta != 0 {
// Handle netpoll events
}
}Memory Management
Garbage Collection Trigger
Sysmon secara berkala memeriksa apakah perlu memaksa garbage collection. Jika sudah lebih dari forcegcperiod (2 menit) sejak GC terakhir, sysmon akan memicu GC paksa.
// Check if we need to force a GC
if t := (gcTrigger{kind: gcTriggerTime, now: now}); t.test() && forcegc.idle.Load() {
lock(&forcegc.lock)
forcegc.idle.Store(false)
var list gList
list.push(forcegc.g)
injectglist(&list)
unlock(&forcegc.lock)
}Memory Scavenging
Sysmon juga bertanggung jawab untuk mengembalikan memori yang tidak digunakan ke sistem operasi.
// Scavenge free memory
if scavengeThreshold > 0 {
scavenge(scavengeThreshold)
}Implementation Details
Startup
Sysmon dimulai di akhir fungsi runtime.main, setelah semua inisialisasi selesai:
func main() {
// Initialize runtime
...
// Start sysmon
systemstack(func() {
newm(sysmon, nil, -1)
})
// Start user goroutines
...
}Fungsi newm membuat thread OS baru untuk menjalankan sysmon.
Main Loop
Loop utama sysmon terlihat seperti ini:
func sysmon() {
var (
idle uint32
lastScavenge int64
scavengeSleep uint32
forceGCIdle int64
)
for {
// Calculate sleep time based on idle state
sleep := calculateSleep(&idle)
// Perform monitoring tasks
checkPreempt()
checkNetpoll()
checkForceGC()
scavengeMemory()
// Sleep
usleep(sleep)
}
}Sleep Time Calculation
Waktu tidur sysmon disesuaikan berdasarkan status idle program:
func calculateSleep(idle *uint32) uint32 {
if *idle == 0 {
*idle = 1
return 20 * 1000 // 20 microseconds
}
if *idle < 10 {
*idle++
}
delay := uint32(20 * 1000 * *idle)
if delay > 10*1000*1000 { // Cap at 10ms
delay = 10 * 1000 * 1000
}
return delay
}Ketika program sibuk, sysmon akan lebih sering terbangun untuk merespons dengan cepat. Ketika program idle, sysmon akan mengurangi frekuensi untuk menghemat CPU.
Summary
Sysmon adalah komponen penting runtime Go, berfungsi sebagai "watchdog" untuk program Go. Meskipun kodenya relatif sederhana, ia memainkan peran kunci dalam:
- Menjaga fairness scheduling dengan preempt goroutine yang berjalan terlalu lama
- Memastikan garbage collection berjalan tepat waktu
- Mengelola memori secara efisien dengan scavenging
- Menangani event network yang timeout
Karena sysmon berjalan di thread terpisah, ia dapat terus bekerja bahkan ketika semua P sedang sibuk, memastikan monitoring yang andal.
