返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.2.2 StopTheWorld

发布于 2024-10-12 19:16:05 字数 3562 浏览 0 评论 0 收藏 0

STW 面向全局设置停止状态,让所有 P 停止任务执行。

// proc.go

func stopTheWorldWithSema() {
    lock(&sched.lock)
    
    sched.stopwait = gomaxprocs           // 停止计数器。
    atomic.Store(&sched.gcwaiting, 1)     // 会被 schedule 看到,引发 gcstopm 调用。
    preemptall()                          // 向所有运行中的 G 发出抢占信号。
    
    // 停止当前 P。
    _g_.m.p.ptr().status = _Pgcstop
    sched.stopwait--
    
    // 回收 syscall P。
    for _, p := range allp {
        s := p.status
        if s == _Psyscall && atomic.Cas(&p.status, s, _Pgcstop) {
            sched.stopwait--
        }
    }
    
    // 停止 idle P。
    for {
        p := pidleget()
        if p == nil {
            break
        }
        
        p.status = _Pgcstop
        sched.stopwait--
    }
    
    wait := sched.stopwait > 0
    unlock(&sched.lock)
    
    // 等待所有 P 停止。
    if wait {
        for {
            // 等待 100us。如被 notewakeup,那么 notetsleep 返回 true。
            if notetsleep(&sched.stopnote, 100*1000) {
                noteclear(&sched.stopnote)
                break
            }
            
            preemptall()
        }
    }
}

所设置状态在 schedule 里被检测到。

// proc.go

func schedule() {
top:
    if sched.gcwaiting != 0 {
        gcstopm()
        goto top
    }
}
// proc.go

// Stops the current m for stopTheWorld.
// Returns when the world is restarted.

func gcstopm() {
    _p_ := releasep()                // 交出 P。
    _p_.status = _Pgcstop
    
    sched.stopwait--
    if sched.stopwait == 0 {         // 最后一个让 stopTheWorldWithSema 里的 wait for 终止。
        notewakeup(&sched.stopnote)
    }
    
    stopm()
}

后台监控 sysmon 同样会被休眠(notesleep)。

// proc.go

func sysmon() {
    for {
        usleep(delay)
        now := nanotime()
        next, _ := timeSleepUntil()
        
        if debug.schedtrace <= 0 && (sched.gcwaiting != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs)) {
            if atomic.Load(&sched.gcwaiting) != 0 || atomic.Load(&sched.npidle) == uint32(gomaxprocs) {
                if next > now {
                    atomic.Store(&sched.sysmonwait, 1)
                    notetsleep(&sched.sysmonnote, sleep)
                    ...
                    atomic.Store(&sched.sysmonwait, 0)
                    noteclear(&sched.sysmonnote)
                }
            }
        }
    }
}

StartTheWorld

让 MP 恢复执行。

// proc.go

func startTheWorldWithSema(emitTraceEvent bool) int64 {
    lock(&sched.lock)
    
    // 重新设置 P 数量。
    procs := gomaxprocs
    if newprocs != 0 {
        procs = newprocs
        newprocs = 0
    }
    
    p1 := procresize(procs)    // 返回有任务的 P 链表。
    
    // 解除状态。
    sched.gcwaiting = 0
    
    // 唤醒 sysmon。
    if sched.sysmonwait != 0 {
        sched.sysmonwait = 0
        notewakeup(&sched.sysmonnote)
    }
    
    unlock(&sched.lock)
    
    // 唤醒有任务的 P。
    for p1 != nil {
        p := p1
        p1 = p1.link.ptr()
   
        // 绑定 M,继续执行。
        if p.m != 0 {
            mp := p.m.ptr()
            p.m = 0
            mp.nextp.set(p)
            notewakeup(&mp.park)
        } else {
            // Start M to run P.  Do not start another M below.
            newm(nil, p, -1)
        }
    }
    
    startTime := nanotime()
    
    // 唤醒 idle P(即便没有任务)检查新任务。
    if atomic.Load(&sched.npidle) != 0 && atomic.Load(&sched.nmspinning) == 0 {
        wakep()
    }
    
    return startTime
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据
    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文