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