# CPU Load spike every 7 hours

## 1 CPU load 含义

CPU load 是指一段时间内系统所有 CPU 上，正在处理以及等待CPU处理的进程数之和。

在使用`top`命令检查系统负载的时候，可以看到`Load averages`字段，但是这个字段并不是表示CPU的繁忙程度，而是度量系统整体负载。

`Load averages`采样是从`/proc/loadavg`中获取的:

```erlang
$ cat /proc/loadavg
0.00 0.01 0.05 1/161 29703

每个值的含义依次为：
lavg_1 (0.00) 1-分钟平均负载
lavg_5 (0.01) 5-分钟平均负载
lavg_15(0.05) 15-分钟平均负载
nr_running (1) 在采样时刻，运行队列的任务的数目，与/proc/stat的procs_running表示相同意思，这个数值是当前可运行的内核调度对象（进程，线程）。
nr_threads (161) 在采样时刻，系统中活跃的任务的个数（不包括运行已经结束的任务），即这个数值表示当前存在系统中的内核可调度对象的数量。
last_pid(29703) 系统最近创建的进程的PID，包括轻量级进程，即线程。

假设当前有两个CPU，则每个CPU的当前任务数为0.00/2=0.00
```

如果你看到 load average 数值是 10，则表明平均有 10 个 进程在运行或等待状态。

有可能系统有很高的负载但是 CPU 使用率却很低，或者负载很低而 CPU 利用率很高，

因为这两者没有直接关系。

## 2 Load 周期性波动

这是由于内核计算负载的方式造成的。参见[内核源码](https://github.com/torvalds/linux/blob/master/include/linux/sched/loadavg.h)

```
#define LOAD_FREQ   (5*HZ+1)
```

load 不是每 5 秒检查一次，是每 5.001 检查一次，因此，需要`5*1000*5.001`秒才能回到5秒的倍数。`25005/3600`大约是7小时（6小时56分40秒）。

### 2.1 特定机器？

每台机器都会有这个情况，只是是否明显，即使是空闲的机器，也会有 7 小时 cpu 波动情况。

```
如果定时任务执行脚本时间很短，load 不是每次都能采集到进程正在运行，但随着时间推移，会在 7 小时内采集到
```

### 2.2 这个是内核 BUG 吗？

不是！我们确信，增加额外的毫秒是有意的，以准确覆盖正在进行非常短但紧张的工作的情况。想象一下，load 是以 5 秒的间隔精确测量的。load 测量中根本看不到一个特殊定制的任务，比如等待0.1秒，做4.8秒的高强度工作，然后再等待0.1秒。

### 2.3 这是一个特定的问题吗？

绝对不是，当定期执行相对较短（执行时间 <5s）的任务时，这种情况一定无处不在。

{% hint style="info" %}
执行脚本小于 5s 时，load 检查时，检查的时候，只会在特定的周期才能正好捕获到脚本处于运行中
{% endhint %}

## 4 如何找出系统中 load 高时处于运行队列的进程 <a href="#slide-2" id="slide-2"></a>

> 每秒输出处于 R (运行中的队列)or D (不可中断的睡眠进程)状态的进程

```
#!/bin/bash
LANG=C
PATH=/sbin:/usr/sbin:/bin:/usr/bin

interval=1
length=86400
for i in $(seq 1 $(expr ${length} / ${interval}));do
    date
    LANG=C ps -eTo stat,pid,tid,ppid,comm --no-header | sed -e 's/^ \*//' | perl -nE 'chomp;say if (m!^\S*[RD]+\S*!)'
    date
    cat /proc/loadavg
    echo -e "\n"
    sleep ${interval}
done
```

> 查 CPU 使用率比较高的线程

```
#!/bin/bash
LANG=C
PATH=/sbin:/usr/sbin:/bin:/usr/bin
interval=1
length=86400

for i in $(seq 1 $(expr ${length} / ${interval}));do
    echo "----------------------------------"
    date
    LANG=C ps -eT -o%cpu,pid,tid,ppid,comm | grep -v CPU | sort -n -r | head -20
    date
    LANG=C cat /proc/loadavg
    { LANG=C ps -eT -o%cpu,pid,tid,ppid,comm | sed -e 's/^ *//' | tr -s ' ' | grep -v CPU | sort -n -r | cut -d ' ' -f 1 | xargs -I{} echo -n "{} + " && echo ' 0';  } | bc -l
    sleep ${interval}
done
fuser -k $0
```

## 传送门

{% embed url="<https://blog.avast.com/investigation-of-regular-high-load-on-unused-machines-every-7-hours>" %}

{% embed url="<https://serverfault.com/questions/454745/nagios-load-spike-every-7-hours>" %}

{% embed url="<https://developer.aliyun.com/article/484253>" %}
