sysmon
sysmon è una funzione ordinaria, tradotta letteralmente come system monitor, rimuovendo le parti commentate ci sono solo circa 200 righe di codice. Viene avviato con un thread separato durante la fase di bootstrap del programma, e successivamente continua a monitorare lo stato del programma Go in esecuzione in background e prende le misure corrispondenti. Per quanto riguarda il codice relativo al suo avvio, può essere visualizzato nella funzione runtime.main:
func main() {
...
mp := getg().m
mainStarted = true
systemstack(func() {
newm(sysmon, nil, -1)
})
...
}Il system monitor stesso è solo un ciclo for, l'intervallo di ogni ciclo è di 20 microsecondi, e con l'aumento dell'indice di idle del programma, l'intervallo viene aumentato al massimo a 10 millisecondi. In ogni ciclo, esegue principalmente le seguenti operazioni:
- Assistenza alla schedulazione delle coroutine, prelazione delle coroutine in esecuzione per lungo tempo
- Controllo della situazione della memoria e判断 se è necessario eseguire il garbage collection
- Monitoraggio del network poller, risveglio delle coroutine bloccate in attesa di eventi di rete
- Raccolta di informazioni di profiling e trace
Avvio
L'avvio di sysmon viene completato nella funzione runtime.main, che è la funzione di ingresso del programma Go. Durante l'inizializzazione del programma, viene chiamato il metodo runtime.newm per creare un thread separato per eseguire la funzione sysmon.
func main() {
...
mp := getg().m
mainStarted = true
systemstack(func() {
newm(sysmon, nil, -1)
})
...
}La funzione runtime.newm accetta tre parametri: la funzione da eseguire, il processore P e l'id del thread. Poiché sysmon è un thread separato, non ha bisogno di essere associato a un P, quindi il secondo parametro è nil, e il terzo parametro è -1, indicando che l'id viene allocato automaticamente.
Ciclo di Monitoraggio
Il corpo principale di sysmon è un ciclo infinito, che continua a monitorare lo stato del programma Go fino a quando il programma non termina.
func sysmon() {
var (
idle uint32
now int64
lasttrace int64
schedtick uint32
allocs uint64
forcegcidle bool
lastforcegc int64
forcegcPeriod int64 = 2 * 60 * 1000 * 1000 * 1000 // 2 minutes
)
lock(&sched.lock)
sched.sysmonlock.inc()
unlock(&sched.lock)
for {
now = nanotime()
// 20 微秒初始间隔
delay := uint32(20)
...
// 执行监控任务
// 根据空闲情况调整间隔时间
if idle == 0 {
idle = 1
} else if idle > 10<<10 {
delay = 10 * 1000 * 1000 // 10 毫秒
} else {
delay <<= 1
}
// 休眠
usleep(delay)
}
}Il ciclo di monitoraggio esegue le seguenti operazioni principali:
- Ottiene il tempo corrente
- Imposta l'intervallo di monitoraggio iniziale a 20 microsecondi
- Esegue i task di monitoraggio
- Regola l'intervallo di monitoraggio in base allo stato di idle
- Si addormenta per l'intervallo specificato
Prelazione delle Coroutine
Una delle funzioni più importanti di sysmon è la prelazione delle coroutine che eseguono per troppo tempo. Questo viene completato dalla funzione 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 {
// Prelazione forzata di G se esegue per troppo tempo
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)
}La funzione attraversa tutti i processori P, e se un P sta eseguendo una coroutine da più di 10 millisecondi, triggera la prelazione forzata. Questo è uno dei meccanismi chiave per garantire che nessuna coroutine monopolizzi la CPU per troppo tempo.
Garbage Collection Forzata
sysmon controlla anche se è necessario eseguire il garbage collection. Se il GC non viene eseguito per un lungo periodo (generalmente 2 minuti), forza l'avvio del GC.
// 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)
}Questo garantisce che anche se il programma non alloca molta memoria, il GC venga eseguito periodicamente per pulire gli oggetti garbage.
Monitoraggio del Network Poller
Il network poller è un componente importante del runtime di Go, responsabile della gestione degli eventi di rete I/O. sysmon monitora lo stato del network poller e risveglia le coroutine bloccate in attesa di eventi di rete quando necessario.
if netpollinited() && sched.lastpoll.Load() != 0 {
if list := netpoll(0); !list.empty() {
injectglist(&list)
}
}Questo garantisce che le coroutine bloccate in attesa di eventi di rete vengano risvegliate tempestivamente quando gli eventi si verificano.
Raccolta di Informazioni
sysmon raccoglie anche informazioni di profiling e trace per aiutare gli sviluppatori a comprendere e ottimizzare le prestazioni del programma.
// Raccolta di informazioni di trace
if trace.enabled() && now-lasttrace > 1000*1000*1000 {
trace.flush()
lasttrace = now
}Intervallo di Monitoraggio
L'intervallo di monitoraggio di sysmon è dinamico, si regola in base allo stato di idle del programma. Inizialmente l'intervallo è di 20 microsecondi, e con l'aumento dell'indice di idle, l'intervallo viene gradualmente aumentato, fino a un massimo di 10 millisecondi.
if idle == 0 {
idle = 1
} else if idle > 10<<10 {
delay = 10 * 1000 * 1000 // 10 毫秒
} else {
delay <<= 1
}Questo design consente a sysmon di essere più reattivo quando il programma è occupato, e di ridurre il consumo di risorse quando il programma è idle.
Riepilogo
sysmon è un componente importante del runtime di Go, responsabile del monitoraggio dello stato del programma e dell'esecuzione di task di manutenzione necessari. Le sue funzioni principali includono:
- Prelazione delle coroutine in esecuzione per lungo tempo
- Trigger del garbage collection forzato
- Monitoraggio del network poller
- Raccolta di informazioni di profiling e trace
Sebbene il codice di sysmon sia relativamente semplice, svolge un ruolo cruciale nel garantire il funzionamento stabile e le prestazioni del programma Go.
