0x01 CPU使用率
这个比较好理解,先来说说这个概念。使用率其实也就是一段时间内 使用时间/总时间
直接说CPU的使用率计算方式吧:
CPU在t1和t2时间内的使用率=CPU非空闲时间/CPU总时间*100%=(1-CPU的空闲时间/CPU总时间)*100%
很好理解。比如一个单核CPU,你程序写个死循环,然后很容易把CPU跑到接近100%,因为死循环不会让出时间片,就会一直占用CPU
比如:http://ju.outofmemory.cn/entry/23059
0x02 CPU负载
计算方式
或许你可能已经看了其他的一些博客,里面多数是cpu load比喻成电话亭打电话、大桥上通车等。这个的确很形象,但是却并不准确。因为Linux系统并不是这样简单的计算。并不是简单的平均
CPU Load的计算是使用到了统计学中指数移动平均(英语:exponential moving average,EMA或EXMA)的概念。
为啥这样?因为:
- 移动平均可抚平短期波动,反映出长期趋势或周期。数学上,移动平均可视为一种卷积。
- 各数值的加权影响力随时间而指数式递减,越近期的数据加权影响力越重,但较旧的数据也给予一定的加权值。
总结一下就是这个统计算法不仅考虑了短期也能反映长期趋势,不仅计算了近期的值还考虑的旧的数据。
Linux中具体计算方式代码如下:
calc_load一般是5*Hz就调用一次,(Hz variable is the pulse rate of particular Linux kernel activity,默认值是10ms)
unsigned long avenrun[3];
static inline void calc_load(unsigned long ticks)
{
unsigned long active_tasks; /* fixed-point */
static int count = LOAD_FREQ;
count -= ticks;
if (count < 0) {
count += LOAD_FREQ;
active_tasks = count_active_tasks(); //注意这个函数!!
CALC_LOAD(avenrun[0], EXP_1, active_tasks);
CALC_LOAD(avenrun[1], EXP_5, active_tasks);
CALC_LOAD(avenrun[2], EXP_15, active_tasks);
}
}
真正的计算公式:
#define FSHIFT 11 /* nr of bits of precision */
#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
#define LOAD_FREQ (5*HZ) /* 5 sec intervals */
#define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
#define EXP_5 2014 /* 1/exp(5sec/5min) */
#define EXP_15 2037 /* 1/exp(5sec/15min) */
#define CALC_LOAD(load,exp,n) \
load *= exp; \
load += n*(FIXED_1-exp); \
load >>= FSHIFT;
cpu load的确能够反映出某时间段内活跃进程的量。
函数如下:
/*
* Either called from update_cpu_load() or from a cpu going idle
*/
static void calc_load_account_active(struct rq *this_rq)
{
long nr_active, delta;
nr_active = this_rq->nr_running; //记录在cpu上运行的进程数
nr_active += (long) this_rq->nr_uninterruptible; //记录不可中断的进程数
if (nr_active != this_rq->calc_load_active) {
delta = nr_active - this_rq->calc_load_active;
this_rq->calc_load_active = nr_active;
atomic_long_add(delta, &calc_load_tasks);
}
}
上面函数需要注意:
(直接引用参考里面的话)
- 大多数的Unix系统中的负载只是记录那些处在运行状态和可运行状态的进程,但是Linux有所不同,它会包含那些不可中断的处于睡眠状态的进程。这时当这些进程由于I/O的阻塞而不能够运行,就可能显著的增加cpu的负载。所以在Unix和Linux下的cpu的负载的计算方法是不一样的,在设定监测值的时候也需要特别考率。
- Linux记录cpu负载的时候是将cpu队列中的运行进程数和不可中断进程数都统计在内的,这样在对cpu负载分析的时候就需要考虑不可中断的进程的情况
所以这个函数才是重点啊:在分析机器负载过高的时候,除了需要关注那些活跃的进程,还需要关注那些由于I/O的阻塞变成不可中断状态的进程。很多时候机器负载突然飙升可能就是IO引起的。
至于load的算法了解就行。
另外插入介绍一下上面说的不可中断进程:
(我觉得这个说的比较好,https://cloud.tencent.com/developer/news/376204)
所谓不可中断sleep是指不能立刻响应处理一些信号的sleep进程,唯一可以唤醒进程的只能是一些等待的资源变得可用(如I/O)或者是等待的时间超时(如果超时时间在进程被置为sleep时已经指定)。
不可中断的sleep通常都由设备驱动程序用来等待磁盘或者网络I/O。该进程从系统调用或陷阱中返回后会收到在其sleep期间累积的信号。在Linux系统中ps -l指令使用字母D来表示不可中断sleep的状态。
这个状态一般表示进程正在跟硬件交互,并且交互过程不允许被其他进程或中断打断。kill -9 也无法杀死
负载值大小
一个CPU满负载其值应该是1,超过1就是过载,低于1一般也没啥压力
n个CPU满负载其值应该是n,超过n就是过载,低于n一般没啥压力
机器过载的话,一般就变得很卡,基本都会影响到上面的服务了,要很重视。
0x03 负载和使用率
- 高cpu使用率时,负载不一定很高
一个机器48个核如果全100%的使用率,那么整个机器使用率是100%,但是负载也可能只是48刚好满负载而已。 - 高负载的时候,cpu使用率不一定高
可能由于多个进程运行不可中断的IO,导致活跃进程数量增加,这个时候负载会飚的很高,但是cpu可能很低
0x04 举个栗子
(高负载,低IO的)
机器配置是48核的。
下图是出问题的机器,负载突然变成1000多。吓死个人,但是CPU就30多
一套军体拳:iostat, sar, demsg, mpstat, pidstat, free, vmstat
发现磁盘IO最高也就200MB/s,并不高并没有到瓶颈,网络IO也不高,机器是万兆网卡。最后通过vmstat 发现了端倪,见下图。
r:表示运行和等待CPU时间片的进程数
可以看见已经很高了,爆表了。
而正常的机器上是这个样的:
后来在出问题的机器上ps -ef一看发现有好几百个同样的脚本再跑,而这个脚本里面有这么一段:
find / -name ""
OK!,问题找到。本来只需要一个这样的脚本跑。其他的都杀了
可以看见run
参考:
cpu使用率
1 http://www.voidcn.com/article/p-yqnvtups-xu.html
cpu负载
2 https://en.wikipedia.org/wiki/Load_%28computing%29#cite_note-5
3 https://zh.wikipedia.org/wiki/%E7%A7%BB%E5%8B%95%E5%B9%B3%E5%9D%87
4 https://github.com/autowebkit/tech/wiki/CPU%E8%B4%9F%E8%BD%BD
5 http://einverne.github.io/post/2019/03/cpu-load.html
6 https://lotabout.me/2018/how-system-load-is-calculated/
如何处理不可中断进程