Golang 抢占调度流程分析

栏目: Go · 发布时间: 6年前

内容简介:本文针对调度抢占逻辑的源码阅读,如果配合下列菜单食用效果更佳.总体思想:sysmontick结构:

Golang 抢占调度流程分析

一.前置知识

本文针对调度抢占逻辑的源码阅读,如果配合下列菜单食用效果更佳.

  1. 熟悉 golang 的基本知识和语法.
  2. 了解目前golang G/M/P 的概念和goroutine
  3. 了解用户态线程/stackfull协程的基本原理.
  4. 推荐资料: Go Preemptive Scheduler Design Doc
  5. 本文环境: go1.12.5 GOOS=darwin GOARCH=amd64

二. 源码分析总

2.1 总体框架

总体思想:

  1. sysmon中定期扫描正在执行的g列表,筛选出执行时间过长的g并且设置需要被抢占的标签.
  2. 在恰当的地方检测被抢占标记,(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)
}
  1. 通过遍历allp列表来获取正在运行的g.
  2. 状态检测.

    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

forcePreemptNS10ms ,如果超过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 = trueg.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%

Golang 抢占调度流程分析

可以发现 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%

Golang 抢占调度流程分析

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%

Golang 抢占调度流程分析

四.总结

User Code

最后 ending…如有不足请指点,亦可留言或联系 fobcrackgp@163.com.

本文为笃行原创文章首发于 大题小作 ,永久链接: Golang 抢占调度流程分析

https://www.ifobnn.com/golangpreempt.html

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Introduction to Linear Optimization

Introduction to Linear Optimization

Dimitris Bertsimas、John N. Tsitsiklis / Athena Scientific / 1997-02-01 / USD 89.00

"The true merit of this book, however, lies in its pedagogical qualities which are so impressive..." "Throughout the book, the authors make serious efforts to give geometric and intuitive explanations......一起来看看 《Introduction to Linear Optimization》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

RGB CMYK 转换工具
RGB CMYK 转换工具

RGB CMYK 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具