调度相关:协作式调度和抢占式调度的实现和发生的时机是怎样的?
协作式调度和抢占式调度的实现和发生的时机是怎样的? 还请老师帮忙解答一下。
正在回答
先从概念上理解:
协作式调度依靠被调度方主动弃权;
抢占式调度则依靠调度器强制将被调度方中断。
协作式调度发生的时机是在函数序言部分的扩栈检测指令,当检测到 stackGuard0 是一个特定值 stackPreempt 的时候,就会主动放弃 CPU 执行权。用编译命令 go tool compile -S 可以看到比较指令:
接着就会跳到 morestack 执行。最终会执行到 goschedImpl,将 g 和 m 解绑,将 g 放到全局可运行队列里去。而 m 这时又会去找其他的 g 来执行。
主动调度弃权依赖在函数序言部分插入抢占检测指令,需要有函数调用。有些场景下,我们写出一个 for {i++} 这样的无限循环,就没法抢占了。这在 Go 1.14 之前会出现死机的事故。Go 1.14 实现了信号抢占,就可以解决这种场景。
预告一下,曹大在今年的 gopherchina 上会讲信号抢占这个 topic。
信号抢占的时机是:
当 goroutine 执行时间过长,超过 10ms,sysmon 会检测到,然后向这个 goroutine 所在的 m 发 SIGURG 信号。
2. GC 时会停止所有正在运行的 goroutine,这时也会向 m 发 SIGURG 信号。
m 收到信号后,会转去执行 sighandler 函数,sighandler 函数会做一些手脚(插入了一个抢占函数:asyncPreempt)就马上返回。返回之后,m 不会接着执行 goroutine 的指令了,而是去执行 asyncPreempt 去了,抢占就在这里发生:把当前的 goroutine 扔到全局可运行队列里,m 则去找其他的 goroutine 执行。
函数调用流程如下:
关于这个话题,推荐一篇文章,写得非常好,值得看 10 遍:
https://golang.design/under-the-hood/zh-cn/part2runtime/ch06sched/preemption/
恭喜解决一个难题,获得1积分~
来为老师/同学的回答评分吧
0 星