返回介绍

上卷 程序设计

中卷 标准库

下卷 运行时

源码剖析

附录

4.2.2 STW

发布于 2024-10-12 19:15:57 字数 3446 浏览 0 评论 0 收藏 0

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

// proc.go

// stopTheWorld stops all P's from executing goroutines, interrupting
// all goroutines at GC safe points and records reason as the reason
// for the stop. On return, only the current goroutine's P is running.

func stopTheWorld(reason string) {
	semacquire(&worldsema)
	stopTheWorldWithSema()
}
func stopTheWorldWithSema() {
	_g_ := getg()

	lock(&sched.lock)
    
    // 停止计数器。(停止成功 -1)
	sched.stopwait = gomaxprocs
    
    // 会被 schedule 看到,引发 gcstopm 调用。
	atomic.Store(&sched.gcwaiting, 1)
    
    // 向所有运行中的 G 发出抢占信号。
	preemptall()
    
	// 停止当前 P,并调整。
	_g_.m.p.ptr().status = _Pgcstop // Pgcstop is only diagnostic.
	sched.stopwait--
    
	// 停止 Psyscall 状态的 P。
	for _, p := range allp {
		s := p.status
		if s == _Psyscall && atomic.Cas(&p.status, s, _Pgcstop) {
			p.syscalltick++
			sched.stopwait--
		}
	}
    
	// 停止闲置 P。
	for {
		p := pidleget()
		if p == nil {
			break
		}
		p.status = _Pgcstop
		sched.stopwait--
	}
    
	wait := sched.stopwait > 0
	unlock(&sched.lock)

	// 等待所有 P 停止。
	if wait {
		for {
			// wait for 100us, then try to re-preempt in case of any races
			if notetsleep(&sched.stopnote, 100*1000) {
				noteclear(&sched.stopnote)
				break
			}
			preemptall()
		}
	}
    
    worldStopped()
}

以上操作,会被 P schedule 观察到。

func schedule() {
	_g_ := getg()

top:
	pp := _g_.m.p.ptr()
	pp.preempt = false

	if sched.gcwaiting != 0 {
		gcstopm()
		goto top
	}

    ...
    
	execute(gp, inheritTime)
}
// Stops the current m for stopTheWorld.
// Returns when the world is restarted.

func gcstopm() {
    
	_p_ := releasep()
	_p_.status = _Pgcstop
	sched.stopwait--
    
    // 如果是最后一个待停止 P,则终止 stopTheWorldWithSema 里的 wait 循环。
	if sched.stopwait == 0 {
		notewakeup(&sched.stopnote)
	}
    
	stopm()
}

非但如此,连后台监控 sysmon 也会被停止。

func sysmon() {

	for {
		usleep(delay)
		now := nanotime()
        
		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) {
				next, _ := timeSleepUntil()
				if next > now {
					atomic.Store(&sched.sysmonwait, 1)
                    
					syscallWake = notetsleep(&sched.sysmonnote, sleep)
                    
					atomic.Store(&sched.sysmonwait, 0)
					noteclear(&sched.sysmonnote)
				}
			}
		}

        ...
	}
}

重启

与之相对的是重启 P 任务。

func startTheWorldWithSema(emitTraceEvent bool) int64 {
    
	assertWorldStopped()

    // 重置 allP,返回有任务的 P 链表。
	procs := gomaxprocs
	p1 := procresize(procs)
    
    // 解除状态。
	sched.gcwaiting = 0
    
    // 唤醒 sysmon。
	if sched.sysmonwait != 0 {
		sched.sysmonwait = 0
		notewakeup(&sched.sysmonnote)
	}

	worldStarted()

    // 唤醒有任务的 P。
	for p1 != nil {
		p := p1
		p1 = p1.link.ptr()
        
		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)
		}
	}

	// Wakeup an additional proc in case we have excessive runnable goroutines
	// in local queues or in the global queue. If we don't, the proc will park itself.
	// If we have lots of excessive work, resetspinning will unpark additional procs as necessary.
	wakep()

	return startTime
}

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

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

发布评论

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