runtime调度器pgm

调度器的工作是将一个 G(需要执行的代码)、一个 M(代码执行的地方)和一个 P(代码执行所需要的权限和资源)结合起来。
堆
本节主要通过阅读runtime源码来认识这三个组件到底长的是什么样子,以此加深对 GPM 的理解。go version go1.15.6
src/runtime/HACKING.mdruntime
src/runtime/runtime2.go
G
goroutine协程
g
src/runtime/runtime2.go
gg
Goroutine 字段非常的多,我们这里分段来理解
type g struct {
// Stack parameters.
// stack describes the actual stack memory: [stack.lo, stack.hi).
// stackguard0 is the stack pointer compared in the Go stack growth prologue.
// It is stack.lo+StackGuard normally, but can be StackPreempt to trigger a preemption.
// stackguard1 is the stack pointer compared in the C stack growth prologue.
// It is stack.lo+StackGuard on g0 and gsignal stacks.
// It is ~0 on other goroutine stacks, to trigger a call to morestackc (and crash).
stack stack // offset known to runtime/cgo
// 检查栈空间是否足够的值, 低于这个值会扩张栈, 0是go代码使用的
stackguard0 uintptr // offset known to liblink
// 检查栈空间是否足够的值, 低于这个值会扩张栈, 1是原生代码使用的
stackguard1 uintptr // offset known to liblink
}
stackGoroutine[stack.lo, stack.hi)
// Stack describes a Go execution stack.
// The bounds of the stack are exactly [lo, hi),
// with no implicit data structures on either side.
// 描述go执行栈
// 栈边界为[lo, hi),左包含可不包含,即 lo≤stack<hi
// 两边都没有隐含的数据结构。
type stack struct {
lo uintptr // 该协程拥有的栈低位
hi uintptr // 该协程拥有的栈高位
}
stackguard0 stackguard1
stackguard0StackPreempt
gstackguard0stackguard016SP(stack pointer)stack.lo+StackGuardruntime·morestack_noctxt()
关于对 Stack 的理解可参考这篇文章
type g struct {
preempt bool // preemption signal, duplicates stackguard0 = stackpreempt
preemptStop bool // transition to _Gpreempted on preemption; otherwise, just deschedule
preemptShrink bool // shrink stack at synchronous safe point
}
preemptpreemptStoppreemptShrink
type g struct {
_panic *_panic // innermost panic - offset known to liblink
_defer *_defer // innermost defer
}
_panic_defer
type g struct {
m *m // current m; offset known to arm liblink
sched gobuf
goid int64
}
mschedgoid
gobuf 结构体
type gobuf struct {
// The offsets of sp, pc, and g are known to (hard-coded in) libmach.
// 寄存器 sp,pc和g的偏移量,硬编码在libmach
//
// ctxt is unusual with respect to GC: it may be a
// heap-allocated funcval, so GC needs to track it, but it
// needs to be set and cleared from assembly, where it's
// difficult to have write barriers. However, ctxt is really a
// saved, live register, and we only ever exchange it between
// the real register and the gobuf. Hence, we treat it as a
// root during stack scanning, which means assembly that saves
// and restores it doesn't need write barriers. It's still
// typed as a pointer so that any other writes from Go get
// write barriers.
sp uintptr
pc uintptr
g guintptr
ctxt unsafe.Pointer
ret sys.Uintreg
lr uintptr
bp uintptr // for GOEXPERIMENT=framepointer
}
sppcgobufsppcgctxt写屏障write barriersggobufretbp
gobuf
Goroutine 的状态有以下几种(源码)
_Gidle_Grunnable_Grunning_Gsyscall_Gwaiting_Gmoribund_unused_Gdead_Genqueue_unused_Gcopystack_Gpreempted_Gscan
_Gmoribund_unusedgdb
_Gscan_GrunningGC_Gscan
_Gscanrunnable_Gscanrunning_Gscansyscall_Gscanwaiting_Gscanpreempted
可以看到除了上面提到的两个未使用的状态外一共有14种状态值。许多状态之间是可以进行改变的。如下图所示


type g strcut {
syscallsp uintptr // if status==Gsyscall, syscallsp = sched.sp to use during gc
syscallpc uintptr // if status==Gsyscall, syscallpc = sched.pc to use during gc
stktopsp uintptr // expected sp at top of stack, to check in traceback
param unsafe.Pointer // passed parameter on wakeup
atomicstatus uint32
stackLock uint32 // sigprof/scang lock; TODO: fold in to atomicstatus
}
atomicstatussyscallspGsyscallsched.spsyscallpcGSyscallsched.pcstktopspparamready()stackLock
type g struct {
waitsince int64 // approx time when the g become blocked
waitreason waitReason // if status==Gwaiting
}
waitsincewaitreason
type g struct {
// asyncSafePoint is set if g is stopped at an asynchronous
// safe point. This means there are frames on the stack
// without precise pointer information.
asyncSafePoint bool
paniconfault bool // panic (instead of crash) on unexpected fault address
gcscandone bool // g has scanned stack; protected by _Gscan bit in status
throwsplit bool // must not split stack
}
asyncSafePoint异步安全点truepaniconfaultgcscandone_Gscanthrowsplit
type g struct {
// activeStackChans indicates that there are unlocked channels
// pointing into this goroutine's stack. If true, stack
// copying needs to acquire channel locks to protect these
// areas of the stack.
activeStackChans bool
// parkingOnChan indicates that the goroutine is about to
// park on a chansend or chanrecv. Used to signal an unsafe point
// for stack shrinking. It's a boolean value, but is updated atomically.
parkingOnChan uint8
}
activeStackChansparkingOnChan
type g struct {
raceignore int8 // ignore race detection events
sysblocktraced bool // StartTrace has emitted EvGoInSyscall about this goroutine
sysexitticks int64 // cputicks when syscall has returned (for tracing)
traceseq uint64 // trace event sequencer
tracelastp puintptr // last P emitted an event for this goroutine
lockedm muintptr
sig uint32
writebuf []byte
sigcode0 uintptr
sigcode1 uintptr
sigpc uintptr
gopc uintptr // pc of go statement that created this goroutine
ancestors *[]ancestorInfo // ancestor information goroutine(s) that created this goroutine (only used if debug.tracebackancestors)
startpc uintptr // pc of goroutine function
racectx uintptr
waiting *sudog // sudog structures this g is waiting on (that have a valid elem ptr); in lock order
cgoCtxt []uintptr // cgo traceback context
labels unsafe.Pointer // profiler labels
timer *timer // cached timer for time.Sleep
selectDone uint32 // are we participating in a select and did someone win the race?
}
gopcstartpcwaitingtimer
从字段命名来看,许多字段都与trace 有关,不清楚什么意思
type g struct {
// Per-G GC state
// gcAssistBytes is this G's GC assist credit in terms of
// bytes allocated. If this is positive, then the G has credit
// to allocate gcAssistBytes bytes without assisting. If this
// is negative, then the G must correct this by performing
// scan work. We track this in bytes to make it fast to update
// and check for debt in the malloc hot path. The assist ratio
// determines how this corresponds to scan work debt.
gcAssistBytes int64
}
gcAssistBytes
gcAssistBytesruntime.mallocgc
总结
atomicstatusatomicstatusschedgobufpreemptpreemptStoppremptShrinkgoiddeferpanicwaitsincewaitreasongcscandoneatomicstatus_Gscan_Grunning
P
p
P 的数量决定了系统内最大可并行的 G 的数量(前提:物理 CPU 核数 >= P 的数量)。
P 的数量由用户设置的 GoMAXPROCS 决定,但是不论 GoMAXPROCS 设置为多大,P 的数量最大为 256。
P的数据结构也有几十个字段,我们还是分开来理解
type p struct {
id int32
status uint32 // one of pidle/prunning/...
link puintptr
schedtick uint32 // incremented on every scheduler call
syscalltick uint32 // incremented on every system call
sysmontick sysmontick // last tick observed by sysmon
}
idstatuslinkschedticksyscallticksysmontick
对于P的状态有五种:
_Pidle_Prunning_Pidle_Psyscall_Pgstop_Psyscall_Pgcstop_Pdead
type p struct {
m muintptr // back-link to associated m (nil if idle)
mcache *mcache
pcache pageCache
raceprocctx uintptr
}
m_Pidlemcachepcacheraceprocctx
mcachepcache
mcache是为了当G与P关联后,执行go code时,会为一些小对象(<32K)分配内存,这时直接从P.mcache 申请,避免直接从os申请,这样就允许多个P并发执行,减少申请内存的锁粒度,参考这里。
type p struct {
deferpool [5][]*_defer // pool of available defer structs of different sizes (see panic.go)
deferpoolbuf [5][32]*_defer
}
deferpooldeferpoolbuf
这两个字段是与 defer 相关
type p struct {
// Cache of goroutine ids, amortizes accesses to runtime·sched.goidgen.
goidcache uint64
goidcacheend uint64
}
goidcachegoidcacheend
两个都是 goroutine ids 的缓存。
type p struct {
// Queue of runnable goroutines. Accessed without lock.
runqhead uint32
runqtail uint32
runq [256]guintptr
// runnext, if non-nil, is a runnable G that was ready'd by
// the current G and should be run next instead of what's in
// runq if there's time remaining in the running G's time
// slice. It will inherit the time left in the current time
// slice. If a set of goroutines is locked in a
// communicate-and-wait pattern, this schedules that set as a
// unit and eliminates the (potentially large) scheduling
// latency that otherwise arises from adding the ready'd
// goroutines to the end of the run queue.
runnext guintptr
}
runqheadrunqtailrunq
runq
runnextnilnilrunqrunq
Prunnext
If a set of goroutines is locked in a communicate-and-wait pattern, this schedules that set as a
// unit and eliminates the (potentially large) scheduling
// latency that otherwise arises from adding the ready’d goroutines to the end of the run queue.
256
type p struct {
// Available G's (status == Gdead)
gFree struct {
gList
n int32
}
}
gFree.n
type p struct {
sudogcache []*sudog
sudogbuf [128]*sudog
}
sudogcachesudogbuf
与*sudog 相关,可以看出与goroutine相关。不清楚这两个字段与上面的 runq 作用是什么。
type p strcut {
// Cache of mspan objects from the heap.
mspancache struct {
// We need an explicit length here because this field is used
// in allocation codepaths where write barriers are not allowed,
// and eliminating the write barrier/keeping it eliminated from
// slice updates is tricky, moreso than just managing the length
// ourselves.
len int
buf [128]*mspan
}
}
mspancache
type p struct {
tracebuf traceBufPtr
// traceSweep indicates the sweep events should be traced.
// This is used to defer the sweep start event until a span
// has actually been swept.
traceSweep bool
// traceSwept and traceReclaimed track the number of bytes
// swept and reclaimed by sweeping in the current sweep loop.
traceSwept, traceReclaimed uintptr
}
与trace相关
type p struct {
palloc persistentAlloc // per-P to avoid mutex
_ uint32 // Alignment for atomic fields below
// The when field of the first entry on the timer heap.
// This is updated using atomic functions.
// This is 0 if the timer heap is empty.
timer0When uint64
}
palloc_timer0When
type p struct {
// Per-P GC state
gcAssistTime int64 // Nanoseconds in assistAlloc
gcFractionalMarkTime int64 // Nanoseconds in fractional mark worker (atomic)
gcBgMarkWorker guintptr // (atomic)
gcMarkWorkerMode gcMarkWorkerMode
// gcMarkWorkerStartTime is the nanotime() at which this mark
// worker started.
gcMarkWorkerStartTime int64
// gcw is this P's GC work buffer cache. The work buffer is
// filled by write barriers, drained by mutator assists, and
// disposed on certain GC state transitions.
gcw gcWork
// wbBuf is this P's GC write barrier buffer.
//
// TODO: Consider caching this in the running G.
wbBuf wbBuf
runSafePointFn uint32 // if 1, run sched.safePointFn at next safe point
}
P 的 GC 状态
gcMarkWrokderStartTimegcwwbBufrunSafePointFnsched.safePointFn
各种状态的切换图

type p strcut {
// Lock for timers. We normally access the timers while running
// on this P, but the scheduler can also do it from a different P.
timersLock mutex
// Actions to take at some time. This is used to implement the
// standard library's time package.
// Must hold timersLock to access.
timers []*timer
// Number of timers in P's heap.
// Modified using atomic instructions.
numTimers uint32
// Number of timerModifiedEarlier timers on P's heap.
// This should only be modified while holding timersLock,
// or while the timer status is in a transient state
// such as timerModifying.
adjustTimers uint32
// Number of timerDeleted timers in P's heap.
// Modified using atomic instructions.
deletedTimers uint32
// Race context used while executing timer functions.
timerRaceCtx uintptr
}
timerLocktimersnumTimersadjustTimerstimerModifiedEarlierdeletedTimersdeletedTimerstimerRaceCtx
timer0WhentimerModifiedEarliesttimersLocktimersnumTimersadjustTimersdeletedTimerstimerDeletedtimerModifiedEarlier
type p struct {
// preempt is set to indicate that this P should be enter the
// scheduler ASAP (regardless of what G is running on it).
preempt bool
pad cpu.CacheLinePad
}
preemptpad
总结
_Pidle_Prunning_Psyscall_Pgcstop_Pdead调度次数系统调用schedticksyscallticknilmrunqrunnablerunnextgoidcachetimersmspanpreempt
M
M 是指OS 内核线程,代表着真正执行计算的资源,在绑定有效的 P 后,进入 schedule 循环;而 schedule 循环的机制大致是从 Global 队列、P 的 Local 队列以及 wait 队列中获取。
m
10000
切记:M 并不保留 G 状态,这是 G 可以跨 M 调度的基础。
下面对 m 的结构体做下介绍
type m struct {
g0 *g // goroutine with scheduling stack
morebuf gobuf // gobuf arg to morestack
divmod uint32 // div/mod denominator for arm - known to liblink
}
g0morebufmorestackdivmod
type m struct {
// Fields not known to debuggers.
procid uint64 // for debuggers, but offset not hard-coded
gsignal *g // signal-handling g
goSigStack gsignalStack // Go-allocated signal handling stack
sigmask sigset // storage for saved signal mask
tls [6]uintptr // thread-local storage (for x86 extern register)
mstartfn func()
curg *g // current running goroutine
caughtsig guintptr // goroutine running during fatal signal
p puintptr // attached p for executing go code (nil if not executing go code)
nextp puintptr
oldp puintptr // the p that was attached before executing a syscall
id int64
}
procidgsignalgoSigStacksigmasksigsettlsmstartfncurgcaughtsigpnilnextpoldpid
caughtsigcurgcurg
pnextpoldp
type m struct {
mallocing int32
throwing int32
preemptoff string // if != "", keep curg running on this m
locks int32
dying int32
profilehz int32
}
throwingthrowpreemptoff
其它几个字段未知
type m struct {
spinning bool // m is out of work and is actively looking for work
blocked bool // m is blocked on a note
newSigstack bool // minit on C thread called sigaltstack
printlock int8
incgo bool // m is executing a cgo call
freeWait uint32 // if == 0, safe to free g0 and delete m (atomic)
fastrand [2]uint32
needextram bool
traceback uint8
}
spinningblockednotenewSigstackprintlockincgofreeWaitfastrandneedextramtraceback
spinning
note 的数据结构为
// sleep and wakeup on one-time events.
// before any calls to notesleep or notewakeup,
// must call noteclear to initialize the Note.
// then, exactly one thread can call notesleep
// and exactly one thread can call notewakeup (once).
// once notewakeup has been called, the notesleep
// will return. future notesleep will return immediately.
// subsequent noteclear must be called only after
// previous notesleep has returned, e.g. it's disallowed
// to call noteclear straight after notewakeup.
// 一次性事件中的休眠和唤醒
// 对于任何调用 notesleep 或 notwakeup 之前,必须调用 noteclear 进行初始化操作。
// 那么,一个线程调用 notesleep, 一个线程调用 notewakup(只能一次)。
// 当 notewakeup 被调用后,notesleep 还未返回,需要过一段时间 notesleep 才能调用完成。在继续调用 noteclear 之前,必须等待当前一个 notesleep 返回后才可以,
// 不允许在 notewakeup 后直接调用 noteclear。
//
// notetsleep is like notesleep but wakes up after
// a given number of nanoseconds even if the event
// has not yet happened. if a goroutine uses notetsleep to
// wake up early, it must wait to call noteclear until it
// can be sure that no other goroutine is calling
// notewakeup.
// notetsleep 类似 notesleep, 但唤醒后会返回一个纳秒数值(如果事件正好还不没有发生)
// 如果一个 goroutine 提前使用了 notetsleep 唤醒,它必须等待调用完 noteclear,直到确认没有其它goroutine调用 notewakeup
//
// notesleep/notetsleep are generally called on g0,
// notetsleepg is similar to notetsleep but is called on user g.
// notesleep/notetsleep 通常在 g0 上调用, notetsleepg类似于notetsleep,但在用户g上调用(可能指的用户态的G)
type note struct {
// Futex-based impl treats it as uint32 key,
// while sema-based impl as M* waitm.
// Used to be a union, but unions break precise GC.
key uintptr
}
notenotesleepnotewakupnoteclear
type m struct {
ncgocall uint64 // number of cgo calls in total
ncgo int32 // number of cgo calls currently in progress
cgoCallersUse uint32 // if non-zero, cgoCallers in use temporarily
cgoCallers *cgoCallers // cgo traceback if crashing in cgo call
}
ncgocallncgocgoCallersUsecgoCaller
这四个字段主要与cgo相关。
type m struct {
park note
alllink *m // on allm
schedlink muintptr
lockedg guintptr
createstack [32]uintptr // stack that created this thread.
lockedExt uint32 // tracking for external LockOSThread
lockedInt uint32 // tracking for internal lockOSThread
nextwaitm muintptr // next m waiting for lock
}
parknotealllinkschedlinklockedgg.lockedmcreatestatcklockedExtlockedIntnextwaitm
type m struct {
waitunlockf func(*g, unsafe.Pointer) bool
waitlock unsafe.Pointer
waittraceev byte
waittraceskip int
startingtrace bool
syscalltick uint32
freelink *m // on sched.freem
}
waitunlockfwaitlockwaittraceevwaittraceskipstartingtracesyscalltickfreelinksched.freem
waitlockfwaitlockwaittraceevwaittraceskipgppark()startingtracefreelink
type m strcut {
// these are here because they are too large to be on the stack
// of low-level NOSPLIT functions.
libcall libcall
libcallpc uintptr // for cpu profiler
libcallsp uintptr
libcallg guintptr
syscall libcall // stores syscall parameters on windows
vdsoSP uintptr // SP for traceback while in VDSO call (0 if not in call)
vdsoPC uintptr // PC for traceback while in VDSO call
}
前五个字段可能与库调用相关,它们之所以出现在这里,是因为它们太大了,不可能出现在低级NOSPLIT函数的堆栈中。
vdsoSPvdsoPCVDSO
type m struct {
// preemptGen counts the number of completed preemption
// signals. This is used to detect when a preemption is
// requested, but fails. Accessed atomically.
preemptGen uint32
// Whether this is a pending preemption signal on this M.
// Accessed atomically.
signalPending uint32
dlogPerM
mOS
// Up to 10 locks held by this m, maintained by the lock ranking code.
locksHeldLen int
locksHeld [10]heldLockInfo
}
preemptGensignalPendingdlogPerMmOSlocksHeldLenlocksHeldheldLockInfo
preemptGensignalPendinglocksHeldLenlocksHeldranking code
heldLockInfo
// heldLockInfo gives info on a held lock and the rank of that lock
// heldLockInfo 提供了一个锁的相关信息和锁的等级
type heldLockInfo struct {
lockAddr uintptr
rank lockRank
}
// src/runtime/lockrank.go type lockRank int
总结
curgnilnextpoldpnote
Sched
go sched
Go 调度器,它维护有存储 M 和 G 的队列以及调度器的一些状态信息等,全局调度时使用。
调度器循环的机制大致是从各种队列、P 的本地队列中获取 G,然后切换到 G 的执行栈上并执行 G 的函数,调用 Goexit 做清理工作并回到 M,如此反复。
下面我们再看一下它的数据结构
type schedt struct {
// accessed atomically. keep at top to ensure alignment on 32-bit systems.
// 原子访问, 最顶部,保证32位系统下的对齐
goidgen uint64
// 上次网络轮询的时间,如果当前正在轮询,则为0
lastpoll uint64 // time of last network poll, 0 if currently polling
// 当前轮询休眠的时间
pollUntil uint64 // time to which current poll is sleeping
lock mutex
// When increasing nmidle, nmidlelocked, nmsys, or nmfreed, be
// sure to call checkdead().
// 当增加 nmidle,nmidlelocked, nmsys或 nmfreed 的时候,一定要调用 checkdead()
// 空闲m等待队列
midle muintptr // idle m's waiting for work
// 空闲m的数量
nmidle int32 // number of idle m's waiting for work
// 等待工作的锁定m的数量
nmidlelocked int32 // number of locked m's waiting for work
// 已创建的m数和下一个m ID, 一个字段代表两个意义?
mnext int64 // number of m's that have been created and next M ID
// 允许的最大m数
maxmcount int32 // maximum number of m's allowed (or die)
// 死锁不计算系统m的数量
nmsys int32 // number of system m's not counted for deadlock
// 累计已释放m的数量
nmfreed int64 // cumulative number of freed m's
// 系统goroutins的数量,原子更新
ngsys uint32 // number of system goroutines; updated atomically
// 空闲p
pidle puintptr // idle p's
npidle uint32
// 自旋, 查看proc.go 文件的 "Worker thread parking/unparking" 注释
nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.
// Global runnable queue.
// 全局运行队列信息, gQueue 是一个通过 g.schedlink 链接的双向队列
runq gQueue
// 队列大小
runqsize int32
// disable controls selective disabling of the scheduler.
//
// Use schedEnableUser to control this.
//
// disable is protected by sched.lock.
disable struct {
// user disables scheduling of user goroutines.
user bool
runnable gQueue // pending runnable Gs
n int32 // length of runnable
}
// Global cache of dead G's.
gFree struct {
lock mutex
stack gList // Gs with stacks
noStack gList // Gs without stacks
n int32
}
// Central cache of sudog structs.
sudoglock mutex
sudogcache *sudog
// Central pool of available defer structs of different sizes.
deferlock mutex
deferpool [5]*_defer
// freem is the list of m's waiting to be freed when their
// m.exited is set. Linked through m.freelink.
// freem 是当 他们的 m.exited 被设置时的等待被释放m列表,通过 m.freelink 链接
freem *m
// gc正在等待运行
gcwaiting uint32 // gc is waiting to run
stopwait int32
stopnote note
sysmonwait uint32
sysmonnote note
// safepointFn should be called on each P at the next GC
// safepoint if p.runSafePointFn is set.
// 如果 p.runSafePointFn 设置的话, safeopintFn 将在每个p下次 GC safepoint 时被调用
safePointFn func(*p)
safePointWait int32
safePointNote note
profilehz int32 // cpu profiling rate
// 对gomaxprocs的最后更改时间
procresizetime int64 // nanotime() of last change to gomaxprocs
totaltime int64 // ∫gomaxprocs dt up to procresizetime
// sysmonlock protects sysmon's actions on the runtime.
//
// Acquire and hold this mutex to block sysmon from interacting
// with the rest of the runtime.
// 在运行时,sysmonlock保护 sysmon 的运行
sysmonlock mutex
}
type schedt struct {
// accessed atomically. keep at top to ensure alignment on 32-bit systems.
// 原子访问, 最顶部位置,保证32位系统下的对齐
goidgen uint64
// 上次网络轮询的时间,如果当前正在轮询,则为0
lastpoll uint64 // time of last network poll, 0 if currently polling
// 当前轮询休眠的时间
pollUntil uint64 // time to which current poll is sleeping
lock mutex
}
goidgenlastpollpollUntillock
type schedt struct {
// When increasing nmidle, nmidlelocked, nmsys, or nmfreed, be
// sure to call checkdead().
// 当增加 nmidle,nmidlelocked, nmsys或 nmfreed 的时候,一定要调用 checkdead() 函数
// 空闲m等待时间
midle muintptr // idle m's waiting for work
// 空闲m的数量
nmidle int32 // number of idle m's waiting for work
// 等待工作的锁定m的数量
nmidlelocked int32 // number of locked m's waiting for work
// 已创建的m数和下一个m ID, 一个字段代表两个意义?
mnext int64 // number of m's that have been created and next M ID
// 允许的最大m数
maxmcount int32 // maximum number of m's allowed (or die)
// 死锁不计算系统m的数量
nmsys int32 // number of system m's not counted for deadlock
// 累计已释放m的数量
nmfreed int64 // cumulative number of freed m's
// 系统goroutins的数量,原子更新
ngsys uint32 // number of system goroutines; updated atomically
}
midlenmidlenmidlelockedmnextmaxmcountnmsysnmfreedngsys
midlenmidlenmidlelockedmaxmcount
type schedt struct {
// 空闲p
pidle puintptr // idle p's
npidle uint32
// 自旋, 查看proc.go 文件的 "Worker thread parking/unparking" 注释
nmspinning uint32 // See "Worker thread parking/unparking" comment in proc.go.
// Global runnable queue.
// 全局运行队列信息, gQueue 是一个通过 g.schedlink 链接的双向队列
runq gQueue
// 队列大小
runqsize int32
}
pidlenpidlenmspinningrunqrunqsize
pidlenpidlenmspinningrunqmidlenmidlepidlenpidlenmspinningrunq
type schedt struct {
// disable controls selective disabling of the scheduler.
//
// Use schedEnableUser to control this.
//
// disable is protected by sched.lock.
disable struct {
// user disables scheduling of user goroutines.
user bool
runnable gQueue // pending runnable Gs
n int32 // length of runnable
}
// Global cache of dead G's.
gFree struct {
lock mutex
stack gList // Gs with stacks
noStack gList // Gs without stacks
n int32
}
}
disablesched.lockgFreepp.gFree_p_.gFree.n >= 64p.gFreegsched.gFree
disableschedEnableUser
type schedt struct {
// Central cache of sudog structs.
sudoglock mutex
sudogcache *sudog
// Central pool of available defer structs of different sizes.
deferlock mutex
deferpool [5]*_defer
// freem is the list of m's waiting to be freed when their
// m.exited is set. Linked through m.freelink.
// freem 是当 他们的 m.exited 被设置时的等待被释放m列表,通过 m.freelink 链接
freem *m
}
sudoglocksudogcachedeferlockdeferpoolfreemm.exited
一个sudog缓存,另一个是defer pool,各自都有一个对应的锁。
type schedt strcut {
// gc正在等待运行
gcwaiting uint32 // gc is waiting to run
stopwait int32
stopnote note
sysmonwait uint32
sysmonnote note
}
gcwaiting
type schedt strcut {
// safepointFn should be called on each P at the next GC
// safepoint if p.runSafePointFn is set.
// 如果 p.runSafePointFn 设置的话, safeopintFn 将在每个p下次 GC safepoint 时被调用
safePointFn func(*p)
safePointWait int32
safePointNote note
profilehz int32 // cpu profiling rate
// 对gomaxprocs的最后更改时间
procresizetime int64 // nanotime() of last change to gomaxprocs
totaltime int64 // ∫gomaxprocs dt up to procresizetime
// sysmonlock protects sysmon's actions on the runtime.
//
// Acquire and hold this mutex to block sysmon from interacting
// with the rest of the runtime.
// 在运行时,sysmonlock保护 sysmon 的运行
sysmonlock mutex
}
sysmonlock

总结
- 调度器会记录当前是否处于轮训状态以及轮训的时间
- 记录有M相关的信息,如当前空闲的M,如果有多个的话,也会记录个数,记录的还有已用过数量,当前有多少个正在spinning,最大允许的M数量。同时也会记录其中持有锁的数量
- 记录有P相关的信息,如当前空闲P,空闲的数量。
- 持有当前系统 groutine 数量
- 有一个G的全局运行队列及其队列大小
- 通过gFree 记录有多少个空闲的G,可以被重复利用
- sudog缓存 和 deferpool
- 都有一个全局锁(lock)和 sysmon (sysmonlock)锁及其它锁(sugoglock)
- 可以控制是否禁用用户gorutine的调度行为,字段 disable(调用 schedEnableUser)
如果想学习Golang 的内存管理,则推荐先看这一篇 https://blog.haohtml.com/archives/29385
参考