背景介绍
客户反馈在系统刚Bringup阶段,没有添加任何业务逻辑情况下,系统负载指标(loadavg)过高,担心对后续业务逻辑有影响,需要优化系统的cpu loadavg指标,本文记录分析cpu loadavg指标过高问题以及解决方法。问题分析
loadavg指标
Linux loadavg指标反映的是Linux 机器在过去一段时间的平均负载,是排查 Linux 性能问题时的重要参考指标之一,可以通过man 5 proc查看到该指标的解读情况如下:.../proc/loadavg The first three fields in this file are load average figures giving the number of jobs in the run queue (state R) or waiting for disk I/O (state D) averaged over 1, 5, and 15 minutes. They are the same as the load average numbers given by uptime(1) and other programs. The fourth field consists of two numbers separated by a slash (/). The first of these is the number of currently runnable kernel scheduling enti‐ ties (processes, threads). The value after the slash is the number of kernel scheduling entities that currently exist on the system. The fifth field is the PID of the process that was most recently created on the system....
前 3 列分别表示这台机器在过去 1、5、15 分内的负载平均,为方便起见,下文分别用load1/load5/load15 表示。load1 的精度是这三个里面最高的, 因此本文接下来将主要关注 load1(后面将看到,load5/load15 算法 load1 一样,只是时间尺寸不同)。需要注意的是,loadavg 是机器的所有 CPU 上所有任务的总负载,因此跟机器的 CPU 数量有直接关系。 CPU 数量不一样的机器,直接比较 loadavg 是没有意义的。为了不同机器之间能够直接对比, 可以将 load1 除以机器的 CPU 数量,得到的指标用 load1_per_core 表示。loadavg指标计算原理介绍
loadavg 算法本质上很简单,但 Linux 内核为了减少计算开销、适配不同处理器平台等等,做了很多工程优化, 所以现在很难快速看懂了。算法实现主要在 kernel/sched/loadavg.c,在该文件中,有计算方法的说明,没有必要去看代码的具体实现细节。其中的 avenrun[] 就是 loadavg。真的loadavg计算,具体说明如下:loadavg 是 nr_running + nr_uninterruptible 状态线程数量的一个指数衰减平均算法每隔 5s(LOAD_FREQ)根据公式计算一次过去 n 分钟内的 loadavg,其中 n 有三个取值:1/5/15对于 1 分钟粒度的 loadavg,公式简化为 avenrun[n] = avenrun[0] * exp_1 + nr_active * (1 - exp_1)因此从计算方式上看,影响loadavg的最主要因素就是nr_running + nr_uninterruptible 状态线程数量。客户问题分析
回到客户问题上,客户反馈loadavg指标偏高,提供了对应top的截图如下:这是一个单核系统,load1接近10,但绝大部分cpu都是处于idle状态,结合loadavg的计算方式,可以分析出应该是较多的任务处于D状态导致的。查看系统D状态的进程如下:console:/ps -eo pid,stat,comm | grep "D" PID STAT COMMAND 5 D kworker/0:0+eve 28 D rpc-selftest 31 D rpc-selftest 33 D rpc-selftest 82 D virtio-service- 83 D virtio-msg-thre 84 D virtio-service- 85 D virtio-msg-thre 127 D rpmsg_propdev_r 129 D prop_set_list
系统的确存在10个左右D状态任务,与负载统计一致,因此就是这些任务长时间处于D状态,导致系统统计的loadavg指标偏高,查看这些任务处于D状态原因,发现是由于编码不规范,很多任务调用了wait_for_completion()接口去长时间等待完成量,在这个接口中,会把任务状态置成D状态,但其实这些任务是可以设置成S状态去等待的,因此把wait_for_completion()接口改成wait_for_completion_interruptible()接口即可。