内容简介:本文针对调度抢占逻辑的源码阅读,如果配合下列菜单食用效果更佳.总体思想:sysmontick结构:
Golang 抢占调度流程分析
一.前置知识
本文针对调度抢占逻辑的源码阅读,如果配合下列菜单食用效果更佳.
- 熟悉 golang 的基本知识和语法.
- 了解目前golang G/M/P 的概念和goroutine
- 了解用户态线程/stackfull协程的基本原理.
- 推荐资料: Go Preemptive Scheduler Design Doc
- 本文环境: go1.12.5 GOOS=darwin GOARCH=amd64
二. 源码分析总
2.1 总体框架
总体思想:
- sysmon中定期扫描正在执行的g列表,筛选出执行时间过长的g并且设置需要被抢占的标签.
- 在恰当的地方检测被抢占标记,(runtime主动)切换,让出cpu.
2.2 标记待抢占G
2.2.1 关键数据结构
sysmontick结构:
顾名思义 sysmontick
用来保存 sysmon抢占检测的上下文.
type sysmontick struct { schedtick uint32 //标记某个g的调度次数 schedwhen int64 //某个g最近一次的调度时间戳 syscalltick uint32 //某个g的系统调用次数 syscallwhen int64 //某个g最近一次系统调用时间戳 }
p结构:
这里只摘取P结构中与抢占相关的字段
type p struct { lock mutex ...... status uint32 // one of pidle/prunning/... schedtick uint32 // incremented on every scheduler call syscalltick uint32 // incremented on every system call sysmontick sysmontick //即上面sysmontick,sysmon会操作这个数据,用于表示最近一次的检测上下文. ...... }
2.2.2 扫描&标记
sysmon
其实干了很多事情,现在只关注两个点:
- 扫描的周期
- 标记的逻辑
func sysmon() { ...... idle := 0 // how many cycles in succession we had not wokeup somebody delay := uint32(0) for { if idle == 0 { // start with 20us sleep... delay = 20 } else if idle > 50 { // start doubling the sleep after 1ms... delay *= 2 } if delay > 10*1000 { // up to 10ms delay = 10 * 1000 } usleep(delay) ...... // retake P's blocked in syscalls // and preempt long running G's if retake(now) != 0 { idle = 0 } else { idle++ } } }
关于扫描周期,至少是 20us
一个循环,后面视 idle
循环次数来进行 指数退避
(超过1ms之后倍增),但最长时间不超过 10ms
,故系统至多在 10ms
左右进行一次抢占检测.
接下来看抢占的具体检测标记逻辑 retake
// 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 lock(&allpLock) for i := 0; i < len(allp); i++ { _p_ := allp[i] if _p_ == nil { continue } pd := &_p_.sysmontick s := _p_.status if s == _Psyscall { ...... } else if s == _Prunning { // Preempt G if it's running for too long. t := int64(_p_.schedtick) if int64(pd.schedtick) != t { pd.schedtick = uint32(t) pd.schedwhen = now continue } if pd.schedwhen+forcePreemptNS > now { continue } preemptone(_p_) } } unlock(&allpLock) return uint32(n) }
- 通过遍历allp列表来获取正在运行的g.
-
状态检测.
t := int64(_p_.schedtick) if int64(pd.schedtick) != t { //在周期内已经调度过,即当前p上运行的g改变过. pd.schedtick = uint32(t) pd.schedwhen = now //更新最近一次抢占检测的时间 continue } if pd.schedwhen+forcePreemptNS > now { continue } preemptone(_p_)
从上面关键数据结构得知 p.schedtick 记录了这个P上总共调度次数(递增), 故 sysmon
通过比较最近一次记录的 schedtick
即可判断在一个周期内是否发生过调度行为.
通过最近一次检测时间与当前时间比较来明确是否需要抢占标记 pd.schedwhen+forcePreemptNS>now
forcePreemptNS
为 10ms
,如果超过10ms没有调度,则需要抢占, PS:并不能保证一个G最多运行10ms.
最后通过 preemptone
来标记当前G需要被抢占
func preemptone(_p_ *p) bool { mp := _p_.m.ptr() if mp == nil || mp == getg().m { return false } gp := mp.curg if gp == nil || gp == mp.g0 { return false } gp.preempt = true // Every call in a go routine checks for stack overflow by // comparing the current stack pointer to gp->stackguard0. // Setting gp->stackguard0 to StackPreempt folds // preemption into the normal stack overflow check. gp.stackguard0 = stackPreempt return true }
核心关键就是设置 g.preempt
= true
和 g.stackguard0
= stackPreempt
.
2.3 抢占触发
func preemptone
注释已经说了(runtime的注释很好),抢占触发时机: 目标g进行函数调用中触发栈检测过程中进行.
func testfunc()(sum int){ var nums[100] int for _, num := range nums { sum += num } return }
"".testfunc STEXT size=132 args=0x8 locals=0x328 0x0000 00000 (main.go:60) TEXT "".testfunc(SB), ABIInternal, $808-8 0x0000 00000 (main.go:60) MOVQ (TLS), CX 0x0009 00009 (main.go:60) LEAQ -680(SP), AX 0x0011 00017 (main.go:60) CMPQ AX, 16(CX) 0x0015 00021 (main.go:60) JLS 122 0x0017 00023 (main.go:60) SUBQ $808, SP 0x001e 00030 (main.go:60) MOVQ BP, 800(SP) 0x0026 00038 (main.go:60) LEAQ 800(SP), BP 0x002e 00046 (main.go:60) FUNCDATA $0, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x002e 00046 (main.go:60) FUNCDATA $1, gclocals·33cdeccccebe80329f1fdbee7f5874cb(SB) 0x002e 00046 (main.go:60) FUNCDATA $3, gclocals·39825eea4be6e41a70480a53a624f97b(SB) 0x002e 00046 (main.go:62) PCDATA $2, $1 0x002e 00046 (main.go:62) PCDATA $0, $0 0x002e 00046 (main.go:62) LEAQ ""..autotmp_3(SP), DI 0x0032 00050 (main.go:62) XORPS X0, X0 0x0035 00053 (main.go:62) PCDATA $2, $0 0x0035 00053 (main.go:62) LEAQ -32(DI), DI 0x0039 00057 (main.go:62) DUFFZERO $64 0x004c 00076 (main.go:62) XORL AX, AX 0x004e 00078 (main.go:62) XORL CX, CX 0x0050 00080 (main.go:62) JMP 92 0x0052 00082 (main.go:62) MOVQ ""..autotmp_3(SP)(AX*8), DX 0x0056 00086 (main.go:62) INCQ AX 0x0059 00089 (main.go:63) ADDQ DX, CX 0x005c 00092 (main.go:62) CMPQ AX, $100 0x0060 00096 (main.go:62) JLT 82 0x0062 00098 (main.go:65) MOVQ CX, "".sum+816(SP) 0x006a 00106 (main.go:65) MOVQ 800(SP), BP 0x0072 00114 (main.go:65) ADDQ $808, SP 0x0079 00121 (main.go:65) RET 0x007a 00122 (main.go:65) NOP 0x007a 00122 (main.go:60) PCDATA $0, $-1 0x007a 00122 (main.go:60) PCDATA $2, $-1 0x007a 00122 (main.go:60) CALL runtime.morestack_noctxt(SB) //扩充stack 0x007f 00127 (main.go:60) JMP 0
具体调用链路为: morestack_noctxt=>runtime.morestack=>runtime·newstack
func newstack() { ...... ...... preempt := atomic.Loaduintptr(&gp.stackguard0) == stackPreempt // Be conservative about where we preempt. // We are interested in preempting user Go code, not runtime code. // If we're holding locks, mallocing, or preemption is disabled, don't // preempt. // This check is very early in newstack so that even the status change // from Grunning to Gwaiting and back doesn't happen in this case. // That status change by itself can be viewed as a small preemption, // because the GC might change Gwaiting to Gscanwaiting, and then // this goroutine has to wait for the GC to finish before continuing. // If the GC is in some way dependent on this goroutine (for example, // it needs a lock held by the goroutine), that small preemption turns // into a real deadlock. if preempt { //注意点. if thisg.m.locks != 0 || thisg.m.mallocing != 0 || thisg.m.preemptoff != "" || thisg.m.p.ptr().status != _Prunning { // Let the goroutine keep running for now. // gp->preempt is set, so it will be preempted next time. gp.stackguard0 = gp.stack.lo + _StackGuard gogo(&gp.sched) // never return } } ...... ...... ...... if preempt { if gp == thisg.m.g0 { throw("runtime: preempt g0") } if thisg.m.p == 0 && thisg.m.locks == 0 { throw("runtime: g is running but p is not") } // Synchronize with scang. casgstatus(gp, _Grunning, _Gwaiting) if gp.preemptscan { ...... } // Act like goroutine called runtime.Gosched. casgstatus(gp, _Gwaiting, _Grunning ) gopreempt_m(gp) // never return } ...... }
最终 gopreempt_m
来抢占当前的g, gopreempt_m
最终调用的还是 goschedImpl
.
runnable schedule()
func gopreempt_m(gp *g) { if trace.enabled { traceGoPreempt() } goschedImpl(gp) } func goschedImpl(gp *g) { status := readgstatus(gp) if status&^_Gscan != _Grunning { dumpgstatus(gp) throw("bad g status") } casgstatus(gp, _Grunning, _Grunnable) dropg() lock(&sched.lock) globrunqput(gp) unlock(&sched.lock) schedule() }
三. 实验
3.1 测试程序
注意点:
- 确保
User Code
部分执行时长能够超过10ms,并且中间不能调用任何会导致切换的代码. - 确保
User Code
能够促发抢占让出逻辑,非内联函数调用,即call时候会调用newstack
- 无其余如网络IO等干扰因素.
package main import ( "runtime" "time" "flag" ) func testfunc()(sum int){ var nums[100] int for _, num := range nums { sum += num } return } func main() { var numOfG = 1 runtime.GOMAXPROCS(2) flag.IntVar(&numOfG, "n", 1, "num of g") flag.Parse() for i:=0 ; i< numOfG ; i++{ go func() { for{ testfunc() } }() } time.Sleep(time.Second*100) }
我们设置GOMAXPROCS=2,来更好的观察上面的分析结果, 并通过死循环模拟 User Code
部分执行过久的情况,采样间隔为1000ms.
3.2实验数据
3.2.1 MAXPROCS>死循环G个数
SCHED 0ms: gomaxprocs=8 idleprocs=6 threads=3 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P1: status=1 schedtick=0 syscalltick=0 m=2 runqsize=0 gfreecnt=0 P2: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P4: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P5: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P6: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P7: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 M2: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=1 G1: status=4(chan receive) m=-1 lockedm=0 G2: status=1() m=-1 lockedm=-1 G3: status=1() m=-1 lockedm=-1 SCHED 1003ms: gomaxprocs=2 idleprocs=1 threads=5 spinningthreads=0 idlethreads=2 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=0 schedtick=2 syscalltick=6 m=-1 runqsize=0 gfreecnt=0 P1: status=1 schedtick=48 syscalltick=10 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=3() m=0 lockedm=-1 SCHED 2005ms: gomaxprocs=2 idleprocs=1 threads=5 spinningthreads=0 idlethreads=2 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=0 schedtick=2 syscalltick=6 m=-1 runqsize=0 gfreecnt=0 P1: status=1 schedtick=91 syscalltick=10 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=3() m=0 lockedm=-1 SCHED 3011ms: gomaxprocs=2 idleprocs=1 threads=5 spinningthreads=0 idlethreads=2 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=0 schedtick=2 syscalltick=6 m=-1 runqsize=0 gfreecnt=0 P1: status=1 schedtick=135 syscalltick=10 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=3() m=0 lockedm=-1 SCHED 4015ms: gomaxprocs=2 idleprocs=1 threads=5 spinningthreads=0 idlethreads=2 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=0 schedtick=2 syscalltick=6 m=-1 runqsize=0 gfreecnt=0 P1: status=1 schedtick=179 syscalltick=10 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=4 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=2() m=3 lockedm=-1 G5: status=3() m=0 lockedm=-1
Goroutine | Total | Execution | Network wait | Sync block | Blocking syscall | Scheduler wait | GC sweeping | GC pause | |
---|---|---|---|---|---|---|---|---|---|
5002ms | 4999ms | 0ns | 0ns | 0ns | 2485µs | 0ns (0.0%) | 0ns (0.0% |
可以发现 G4
为死循环的G,在 P0
上执行,每一秒中新增的 schedtick
约为50, 也就是实际调度约为20ms一次.
3.2.2 MAXPROCS=死循环G个数
SCHED 0ms: gomaxprocs=8 idleprocs=6 threads=3 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P1: status=1 schedtick=0 syscalltick=0 m=2 runqsize=0 gfreecnt=0 P2: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P4: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P5: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P6: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P7: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 M2: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=1 G1: status=4(chan receive) m=-1 lockedm=0 G2: status=1() m=-1 lockedm=-1 G3: status=1() m=-1 lockedm=-1 SCHED 1006ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=47 syscalltick=6 m=2 runqsize=0 gfreecnt=0 P1: status=1 schedtick=49 syscalltick=8 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=4 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=6 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=2() m=2 lockedm=-1 G5: status=2() m=3 lockedm=-1 G6: status=3() m=0 lockedm=-1 SCHED 2013ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=91 syscalltick=6 m=2 runqsize=0 gfreecnt=0 P1: status=1 schedtick=93 syscalltick=8 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=4 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=6 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=2() m=3 lockedm=-1 G5: status=2() m=2 lockedm=-1 G6: status=3() m=0 lockedm=-1 SCHED 3018ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=135 syscalltick=6 m=2 runqsize=0 gfreecnt=0 P1: status=1 schedtick=137 syscalltick=8 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=4 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=6 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=2() m=3 lockedm=-1 G5: status=2() m=2 lockedm=-1 G6: status=3() m=0 lockedm=-1 SCHED 4029ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=179 syscalltick=6 m=2 runqsize=0 gfreecnt=0 P1: status=1 schedtick=181 syscalltick=8 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=6 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=1() m=-1 lockedm=-1 G6: status=3() m=0 lockedm=-1 SCHED 5038ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=2 runqueue=1 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=226 syscalltick=8 m=0 runqsize=0 gfreecnt=0 P1: status=1 schedtick=227 syscalltick=8 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=4 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=0 curg=5 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 G1: status=4(semacquire) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=1(trace reader (blocked)) m=-1 lockedm=-1 G4: status=2() m=3 lockedm=-1 G5: status=2() m=0 lockedm=-1 G6: status=4(timer goroutine (idle)) m=-1 lockedm=-1
G4/G5为死循环G, 与第一个实验数据类似.
Goroutine | Total | Execution | Network wait | Sync block | Blocking syscall | Scheduler wait | GC sweeping | GC pause | |
---|---|---|---|---|---|---|---|---|---|
5021ms | 5019ms | 0ns | 0ns | 0ns | 2156µs | 0ns (0.0%) | 0ns (0.0%) | ||
5021ms | 5018ms | 0ns | 0ns | 0ns | 2442µs | 0ns (0.0%) | 0ns (0.0% |
3.2.3 MAXPROCS<死循环G个数
我们通过开启5个死循环G来实验.
SCHED 0ms: gomaxprocs=8 idleprocs=5 threads=5 spinningthreads=1 idlethreads=0 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=0 syscalltick=0 m=3 runqsize=0 gfreecnt=0 P1: status=1 schedtick=1 syscalltick=0 m=2 runqsize=0 gfreecnt=0 P2: status=1 schedtick=0 syscalltick=0 m=4 runqsize=0 gfreecnt=0 P3: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P4: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P5: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P6: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 P7: status=0 schedtick=0 syscalltick=0 m=-1 runqsize=0 gfreecnt=0 M4: p=2 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=true blocked=false lockedg=-1 M3: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=true blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=1 G1: status=1(chan receive) m=-1 lockedm=0 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 SCHED 1000ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=3 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=48 syscalltick=9 m=2 runqsize=0 gfreecnt=0 P1: status=1 schedtick=49 syscalltick=6 m=3 runqsize=0 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=7 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=4 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=9 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=2() m=2 lockedm=-1 G5: status=1() m=-1 lockedm=-1 G6: status=1() m=-1 lockedm=-1 G7: status=2() m=3 lockedm=-1 G8: status=1() m=-1 lockedm=-1 G9: status=3() m=0 lockedm=-1 SCHED 2003ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=1 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=91 syscalltick=9 m=2 runqsize=1 gfreecnt=0 P1: status=1 schedtick=92 syscalltick=6 m=3 runqsize=1 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=8 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=7 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=9 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=1() m=-1 lockedm=-1 G6: status=1() m=-1 lockedm=-1 G7: status=2() m=2 lockedm=-1 G8: status=2() m=3 lockedm=-1 G9: status=3() m=0 lockedm=-1 SCHED 3013ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=0 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=134 syscalltick=9 m=2 runqsize=1 gfreecnt=0 P1: status=1 schedtick=135 syscalltick=6 m=3 runqsize=2 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=9 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=1() m=-1 lockedm=-1 G6: status=1() m=-1 lockedm=-1 G7: status=1() m=-1 lockedm=-1 G8: status=1() m=-1 lockedm=-1 G9: status=3() m=0 lockedm=-1 SCHED 4017ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=1 runqueue=1 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=178 syscalltick=9 m=2 runqsize=1 gfreecnt=0 P1: status=1 schedtick=179 syscalltick=6 m=3 runqsize=1 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=7 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=6 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=9 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=1() m=-1 lockedm=-1 G6: status=2() m=2 lockedm=-1 G7: status=2() m=3 lockedm=-1 G8: status=1() m=-1 lockedm=-1 G9: status=3() m=0 lockedm=-1 SCHED 5019ms: gomaxprocs=2 idleprocs=0 threads=5 spinningthreads=0 idlethreads=2 runqueue=1 gcwaiting=0 nmidlelocked=0 stopwait=0 sysmonwait=0 P0: status=1 schedtick=220 syscalltick=9 m=2 runqsize=2 gfreecnt=0 P1: status=1 schedtick=221 syscalltick=6 m=3 runqsize=1 gfreecnt=0 M4: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 M3: p=1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M2: p=0 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M1: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=1 dying=0 spinning=false blocked=false lockedg=-1 M0: p=-1 curg=-1 mallocing=0 throwing=0 preemptoff= locks=0 dying=0 spinning=false blocked=true lockedg=-1 G1: status=4(sleep) m=-1 lockedm=-1 G2: status=4(force gc (idle)) m=-1 lockedm=-1 G3: status=4(GC sweep wait) m=-1 lockedm=-1 G17: status=4(finalizer wait) m=-1 lockedm=-1 G33: status=4(trace reader (blocked)) m=-1 lockedm=-1 G4: status=1() m=-1 lockedm=-1 G5: status=1() m=-1 lockedm=-1 G6: status=1() m=-1 lockedm=-1 G7: status=1() m=-1 lockedm=-1 G8: status=1() m=-1 lockedm=-1 G9: status=1() m=-1 lockedm=-1
当死循环G超过 MAXPROCS
可以明显发现5个死循环的G在轮转切换(抢占, P关联), 并且可以明显观察到全局G队列的变化,执行时长比较均匀.
Goroutine | Total | Execution | Network wait | Sync block | Blocking syscall | Scheduler wait | GC sweeping | GC pause | |
---|---|---|---|---|---|---|---|---|---|
5040ms | 2173ms | 0ns | 0ns | 0ns | 2867ms | 0ns (0.0%) | 0ns (0.0%) | ||
5040ms | 1950ms | 0ns | 0ns | 0ns | 3090ms | 0ns (0.0%) | 0ns (0.0%) | ||
5040ms | 2042ms | 0ns | 0ns | 0ns | 2997ms | 0ns (0.0%) | 0ns (0.0%) | ||
5040ms | 1938ms | 0ns | 0ns | 0ns | 3102ms | 0ns (0.0%) | 0ns (0.0%) | ||
5040ms | 1971ms | 0ns | 0ns | 0ns | 3069ms | 0ns (0.0%) | 0ns (0.0% |
四.总结
User Code
最后 ending…如有不足请指点,亦可留言或联系 fobcrackgp@163.com.
本文为笃行原创文章首发于 大题小作 ,永久链接: Golang 抢占调度流程分析
https://www.ifobnn.com/golangpreempt.html
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- golang 抢占式调度
- Swoole协程抢占式调度
- Golang 并发问题(五)goroutine 的调度及抢占
- Go 1.14 新特性之 Goroutine 抢占式调度
- Go 协作与抢占
- Go 协作与抢占
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
JSON 在线解析
在线 JSON 格式化工具
HSV CMYK 转换工具
HSV CMYK互换工具