golang 异步抢占例子,继上篇文章《golang 非协作式抢占》添加一些手操事例
一定要go1.14以后的版本,本文版本
cat main.go编译
运行
strace关闭异步抢占
GODEBUG=asyncpreemptoff=1 ./maintid 25674strace -p 25674
开启异步抢占
./maintid 3141strace -p 3141
注意到右侧的si_pid 与 tid不同,也就是虽然 GMP中虽然 P被设置了1,但是 M这个时候数量是多于 P的。 发送的信号之后主要做的是保存 ctx,接着 M与P depatch掉。(
GC
GCidle调试器
SIGURGgolang的dlv 会收到影响
比方说我写的go调试器toy,里面需要将 信号忽略
tight loop
forgcflag='-N -l'是有可能把函数调用优化成上述的代码,当然这是另一种go用法的失误(或者说go设计者并不希望你这么用,详细可以看上篇《golang 非协作抢占》里面有几个链接都是比较好的事例)。
其他BUG
代码位置
下面是分析源码,不喜欢看的可以跳过。 先找一下发送信号的位置。先看最最基本的,某个G占用超过10ms的情况,由 monitor 协程发起的。
发送方
retakepreemptone(_p_)在之前的版本协作式抢占中,这个标记设置好了,就只能干等着,直到 morestask (函数调用)或者gopark(IO操作,例如channel之类)才行。但是这里面明显就能看到有一个 preemptM信号通知线程。
接收方
sigPreemptconst sigPreempt = _SIGURG异步安全点pc这部分是汇编实现的,我个人电脑是amd64/linux,所以看的是这个文件,这里面其实就是保存了所有的寄存器,然后偷偷在中间调用了asyncPreempt2(这个是golang实现的),这样让其进行了调度。再偷偷恢复了自己的寄存器。
后面代码就有点小复杂,而且并不是异步抢占的核心逻辑。看代码主要要看三个函数
补一张我画的天师神图,希望能一路驱魔斩妖保平安

注意
sysmon