1、软硬件环境
硬件: 飞腾E2000Q 平台
软件: linux 4.19.246
2、问题现象
系统在上电后,无意中发现系统的平均负载很大,数值显示远超过cpu的承载能力。心想也没有跑什么业务程序呀,吓得赶紧运行top命令,瞅一瞅CPU都在干些啥么,然后4个CPU 都表示我一直在摸鱼呀,啥也没干,100%的空闲,没有做任何事情,而且有图有真相!
系统的average load 表示过去1分钟、5分钟、15分钟的平均负载分别为12、9.4、4.67
这个结果就表示我一个4人团队,你要我做12个人的活??当然这也不是不可以,CPU有自己的队列机制管理,不行就排队呗。
但是CPU在空闲的情况下,为何load会很大呢?
3 问题分析
要搞明白这个问题,首先要搞明白 何种情况下消耗的CPU时间会被计算进average load ?
其实这个问题在之前的文章中已经讲过:深度睡眠和浅度睡眠 这里再简单叙述一下
CPU的load 其实是指 CPU的 任务队列里的 具有的负载量。
比如,任务队列里有1个满负载的任务,load=1 , 如果有2个满负载的任务,load=2 (不管几个核都是2)。但是如果就1个核的话,load > 1 就说明 负载有点重了。但是如果 load = 2 , 有 4个核,说明负载还算轻松,因为还有2个空闲的核(团队还有2个人可以干活)。如果有两个不是满负载的任务,0.3 0.4 这两个任务的负载加起来0.7的负载 ,1个核 就可以很轻松。
但是 load average 只看CPU的话,有些情况下会不准。
比如下面的情况,正常质量高性能高的硬件,计算出来的load average应该低,质量差性能低的硬件计算出来的 load average 应该高。
举例,读磁盘的操作,是包括CPU消耗 + IO消耗的一种操作,同样都是1Ghz 的CPU ,同样的代码,在高速硬盘上和低速软盘上跑起来的结果可能就是不一样。
高速硬盘 80%的时间花费在了CPU上,而低速硬盘,绝大部分时间花在了读磁盘上(CPU等待磁盘IO)。这样计算的就不准了。
所以load的统计就把CPU等待磁盘IO的时间计算上了。
所以,一般 load average 高的话,CPU利用率又很低,要么你用了 task_uninterruptible 要么你的磁盘很慢。因为磁盘的睡眠都是task_uninterruptible的。那为什么磁盘的睡眠是深度睡眠又不能被中断的呢?举个例子就是代码的执行是一边load,一边执行的,这个过程是不能被中断的!
以上我们可知,睡眠的进程/线程( D状态),是会被统计到 CPU的 load里的!
那么我们的思路就来了!
1、看看系统里有没有读写磁盘的操作,有没有CPU在等待IO的情况
2、寻找D状态的进程或者线程,看看到底是谁在搞怪!
我们首排查第1点。
iostat显示 没有磁盘的读写!排除这种情况!
通过ps命令看到两种内核线程是D状态的,他们的睡眠时间会被统计到average load中的
第二个内核线程board是我们其中一个驱动的,禁掉以后,问题依然存在,那么osal_kthread就是最大的嫌疑人了!
我们来看看它到底是何方神圣,在内核代码里我们查询它的蛛丝马迹
这是飞腾CPU SCE security的驱动,该驱动我们用不到,禁掉以后,系统运行2小时,load显示很低了!
到这里,我们已经确定罪魁祸首就是它了!下面我们看看它的内核线程是不是是不是在睡觉。
在它的主体驱动框架里 看到 drivers/crypto/te/merak_driver/common/te_worker.c,在没有工作任务的时候就去睡觉!
从上图可以看出,实际的休眠是在OSAL_COMPLETION_COND_WAIT()完成的, 但是这里依然看不到TASK_UNINTERRUPTIBLE, 还是无法确定是深度睡眠,我们继续往下挖。
OSAL_COMPLETION_COND_WAIT()
--> osal_completion_wait()
-->wait_for_completion()
最后一个函数 wait_for_completion 的睡眠就是 TASK_UNINTERRUPTIBLE的!!!
到这里,验证了下面这句话:
一般 load average 高的话,CPU利用率又很低,要么你用了 task_uninterruptible 要么你的磁盘很慢。
以上,解释了为什么top显示4个核都空闲,load会很大的原因。
4 总结
- 磁盘IO读写在排队的时候,是深度睡眠
- 深度睡眠 uninterruptible sleep 的进程/线程会被标记成D状态,不响应任何信号,杀不掉
- CPU 的load 不光统计CPU消耗的时间,D状态进程睡眠时间也会被统计上。
- 一般 load average 高的话,CPU利用率又很低,要么你用了 task_uninterruptible 要么你的磁盘很慢。