golang 异步抢占例子,继上篇文章《golang 非协作式抢占》添加一些手操事例

一定要go1.14以后的版本,本文版本

cat main.go

编译

运行

strace

关闭异步抢占

GODEBUG=asyncpreemptoff=1 ./maintid 25674
strace -p 25674



开启异步抢占

./maintid  3141
strace -p 3141


注意到右侧的si_pid 与 tid不同,也就是虽然 GMP中虽然 P被设置了1,但是 M这个时候数量是多于 P的。 发送的信号之后主要做的是保存 ctx,接着 M与P depatch掉。(

GC

GCidle

调试器

SIGURG

golang的dlv 会收到影响

比方说我写的go调试器toy,里面需要将 信号忽略


tight loop

for
gcflag='-N -l'

是有可能把函数调用优化成上述的代码,当然这是另一种go用法的失误(或者说go设计者并不希望你这么用,详细可以看上篇《golang 非协作抢占》里面有几个链接都是比较好的事例)。


其他BUG


代码位置

下面是分析源码,不喜欢看的可以跳过。 先找一下发送信号的位置。先看最最基本的,某个G占用超过10ms的情况,由 monitor 协程发起的。

发送方

retake


preemptone(_p_)


在之前的版本协作式抢占中,这个标记设置好了,就只能干等着,直到 morestask (函数调用)或者gopark(IO操作,例如channel之类)才行。但是这里面明显就能看到有一个 preemptM信号通知线程。


接收方

sigPreemptconst sigPreempt = _SIGURG


异步安全点pc




这部分是汇编实现的,我个人电脑是amd64/linux,所以看的是这个文件,这里面其实就是保存了所有的寄存器,然后偷偷在中间调用了asyncPreempt2(这个是golang实现的),这样让其进行了调度。再偷偷恢复了自己的寄存器。


后面代码就有点小复杂,而且并不是异步抢占的核心逻辑。看代码主要要看三个函数


补一张我画的天师神图,希望能一路驱魔斩妖保平安



注意

sysmon