前言 在高性能网络模型下,使用polling模式,依然遇到了长尾20ms+的情况,远高于平均的1ms左右。怀疑是调度的延迟导致的。那么如何量化是不是内核的调度导致的呢?以及如何发现是什么原因导致的呢? 分析 调度延迟 在前文《[Linux][kernel]sched delay和steal time的原理分析以及atop的监控改进》中分析过Linux中如何计算一个task的run delay:即一个task希望运行,但是得不到运行的时间统计,即run delay,也就是调度延迟。 那么问题来了,如果通过atop监控到某一个进程的run delay是2%,能说明那20ms的长尾延迟是因为调度延迟导致的吗?答案是不能。我们看下面的两种情况: 1,例如说,Run 19ms, Delay 1ms,Run 19ms, Delay 1ms,Run 19ms, Delay 1ms。在这个模型下,统计出来的run delay是2%。 2,另外一种模型下,例如 Run 980ms, Delay 20ms, Run 980ms, Delay 20ms,这个模型下,就会遇到20ms+的长尾延迟。 所以atop可以统计出来宏观的run delay延迟占比,但是不能统计出来具体的调度延迟极端情况。 runqslower工具 在bcc中提供了runqslower工具,可以通过参数控制,打印出来哪些进程的调度延迟超过了特定的阈值,例如希望知道哪些进程的run delay超过10ms,可以使用这样的命令:
~# /usr/share/bcc/tools/runqslower 10000
命令执行的效果图如下:
通过这样的命令,我们可以知道TID、COMM(线程级别的名字)和它的具体的调度延迟。通过这样的方法,我们在问题现场上抓到了20ms+的长尾延迟确实是由于调度延迟导致的。 runqslower的改进 尽管知道了长尾延迟的原因,但是还是希望可以发现是由于哪个进程的影响导致了延迟。 可以做这样的猜想:在runqslower发现调度延迟的情况下,必然是由于其他的task的抢占导致。那么可能是某一个task,或者某几个task。那么在抓到了调度延迟的时候,把前面的task也dump出来,那么大概率是可以发现是哪个task抢占的CPU时间。基于此,对于runqslower做了一定的增强:增加’-P’和’--previous’选项,同时打印出来前面的task。可以使用这样的命令:
~# /usr/share/bcc/tools/runqslower 10000 -P
命令的执行效果如下:
可以看到前面执行的哪个任务导致的。在测试的这段中,使用stress进行压力测试:
~# taskset -c 7 stress -c 2
让一个CPU同时跑两个线程,就可以造成它们互相抢占的情况,所以可以看到两个TID互相抢占的情况。 使用改进后的runqslower果然发现了问题:slab的回收操作发生在kthread上,kthread在回收slab的时候使用了较多的CPU时间。 对于runqslower的改进也贡献到了bcc,升级bcc到最新版,或者手工合入这个patch即可:
https://github.com/iovisor/bcc/commit/508d9694ba7ea503cce821175ffca5a7740b832b