文章目录
  1. 1. 平均负载的定义
  2. 2. 进程的状态
    1. 2.1. 操作系统进程的基本状态
    2. 2.2. Linux 进程的基本状态
  3. 3. 理解定义
    1. 3.1. 平均负载与 CPU 利用率
  4. 4. 实验
    1. 4.1. 工具介绍
    2. 4.2. CPU 密集型
    3. 4.3. I/O 密集型
    4. 4.4. 大量等待调度的进程
  5. 5. 小结

平均负载的定义

uptime是最简单的性能分析命令之一,它的输出非常简单,比如:

1
2
$ uptime
16:08:29 up 42 days, 5:11, 2 users, load average: 0.33, 0.22, 0.22

前面几列易于理解,分别是当前时间,系统已运行时间,登录的用户数。可是最后的平均负载是什么?查看uptime帮助文档对其的定义:

System load averages is the average number of processes that are either in a runnable or uninterruptable state. A process in a runnable state is either using the CPU or waiting to use the CPU. A process in uninterruptable state is waiting for some I/O access, eg waiting for disk. The averages are taken over the three time intervals. Load averages are not normalized for the number of CPUs in a system, so a load average of 1 means a single CPU system is loaded all the time while on a 4 CPU system it means it was idle 75% of the time.

大概翻译过来就是:

系统的平均负载是指处于可运行和不可中断状态的进程平均数量。所谓可运行状态是指正在使用或等待使用 CPU,而不可中断状态是指进程正执行某种 I/O 操作,比如读写磁盘。平均负载会计算 1 分钟、5 分钟和 15 分钟内的该指标。不过需要注意的是,该数据没有针对 CPU 个数作归一化处理,所以平均负载 1 在单核系统上意味满负载运行,而在 4 核系统上意味着只使用了 25% 的负载。

进程的状态

操作系统进程的基本状态

在操作系统的概念中,进程会不断改变其运行状态,其必须有以下三种基本状态:

  1. 就绪:进程所需要的资源都已经获得,但是还没有分配到 CPU 来运行
  2. 运行:进程分配到 CPU 在运行
  3. 阻塞:进程的某些资源还没有满足,比如缓冲区申请未分配,等待 I/O 完成

Linux 进程的基本状态

相比于标准的操作系统概念,Linux 中对于状态有自己的划分:

  1. D 不可中断睡眠,最常见的是正在访问硬件设备,若中断会导致磁盘数据和进程数据不一致
  2. R 运行和就绪,对,Linux 上这两个状态是被统计在一起的
  3. S 可中断睡眠,比如进程正在等待信号唤醒
  4. T 停止,是处于调试状态的进程
  5. Z 僵尸进程,即父进程未等待子进程退出

理解定义

由上述 Linux 的进程定义,所谓可运行和不可中断状态就是活跃的进程,即正在使用 CPU/等待 CPU/等待 IO 的进程,而平均负载可理解为单位时间内活跃进程的数量。

如果每个核上都正好跑着一个进程,则说明 CPU 被充分的利用。那么平均负载为 4 在一个四核处理器上就是 CPU 刚好被完全占用,而在单核处理器上,意味着有 3 个进程得不到 CPU,只能干等。

平均负载与 CPU 利用率

在未看过本文之前,你很可能将平均负载和 CPU 利用率划上等号,事实上,这是两个独立的概念。

  • 对于 CPU 密集型进程,因为大量利用了 CPU,故而平均负载升高
  • 对于 I/O 密集型进程,虽然 CPU 利用率不高,但是大量进程都阻塞住,使得活跃进程数量增加,所以平均负载也会升高
  • 对于有大量等待调度的进程,这些进程都处于就绪态,平均负载也高。而且由于上下文切换,进程数量多,切换的机会也多,这无形中也增加了负载

实验

我们就上文提到的三个场景,分别做实验来模拟。

我们实验所用的系统是 ubuntu 18,机器配置为 2 CPU/16 GB。

工具介绍

工欲善其事,必先利其器。有很多现成的工具可以模拟负载,监控和分析系统性能。

  1. stress可以用来对系统施加指定类型的系统压力,它并不是一个基准测试工具
  2. mpstat是一个多核的 CPU 性能分析工具,可以统计每个 CPU 的性能
  3. pidstat是一个多进程性能分析工具,可以统计每个进程的性能

实验开始时首先查看一下系统的负载情况:

1
2
$ uptime
15:23:16 up 51 days, 4:26, 1 user, load average: 0.15, 0.30, 0.31

确认一下系统的是否是 2 核:

1
2
$ cat /proc/cpuinfo | grep processor
2

CPU 密集型

我们利用stress模拟一个 CPU 跑满的场景:

1
$ stress --cpu 1 --timeout 600

过两分钟后查看uptime查看平均负载:

1
2
$ uptime
15:41:09 up 51 days, 4:44, 2 users, load average: 1.18, 0.77, 0.49

可以看到系统负载越升越高,1 分钟内的平均负载高于 5 分钟内平均负载。

使用mpstat查看 CPU 利用率的情况:

1
2
3
4
5
6
# ALL 表示输出所有 CPU 的情况,5 表示每隔 5s 输出一次
$ mpstat -P ALL 5
03:42:26 PM CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
03:42:31 PM all 51.96 0.00 1.01 0.81 0.00 0.10 0.00 0.00 0.00 46.12
03:42:31 PM 0 3.44 0.00 2.02 1.62 0.00 0.20 0.00 0.00 0.00 92.71
03:42:31 PM 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

可以看到其中一个 CPU 利用率飙到了 100%,这个是平均负载升高的直接原因。

使用pidstat查看具体是哪个进程占用了 CPU:

1
2
3
4
5
6
7
8
# 5 表示每隔 5s 输出一次
$ pidstat -u 5
Linux 4.15.0-96-generic (work) 06/07/2020 _x86_64_ (2 CPU)

03:46:19 PM UID PID %usr %system %guest %wait %CPU CPU Command
03:46:24 PM 0 10766 0.20 0.20 0.00 0.60 0.40 0 redis-server
03:46:24 PM 1000 27225 99.80 0.00 0.00 0.00 99.80 1 stress
03:46:24 PM 1000 27583 0.00 0.20 0.00 0.00 0.20 0 pidstat

虽然输出有很多行,但我们很容易就发现是stress这个进程使用了 100% 的 CPU。

I/O 密集型

我们还是使用stress模拟 I/O 压力

1
$ stress -i 1 --timeout 600

过两分钟后查看uptime查看平均负载:

1
2
$ uptime
15:53:43 up 51 days, 4:57, 2 users, load average: 1.09, 0.94, 0.85

平均负载果然升高了。

同时我们看一下 CPU 利用率的情况:

1
2
3
4
03:54:43 PM  CPU    %usr   %nice    %sys %iowait    %irq   %soft  %steal  %guest  %gnice   %idle
03:54:48 PM all 2.57 0.00 3.81 29.35 0.00 1.75 0.00 0.00 0.00 62.51
03:54:48 PM 0 2.49 0.00 3.53 45.95 0.00 3.33 0.00 0.00 0.00 44.70
03:54:48 PM 1 2.86 0.00 4.09 12.88 0.00 0.20 0.00 0.00 0.00 79.96

可以看到两个 CPU 都没有被占满。但是%iowait这一项和上一小节有明显的差异,%iowait即是反映 I/O 压力的情况。

我们接着用pidstat查看具体是哪个进程导致的%iowait升高:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
$ pidstat -u 5
Linux 4.15.0-96-generic (work) 06/07/2020 _x86_64_ (2 CPU)

03:59:31 PM UID PID %usr %system %guest %wait %CPU CPU Command
03:59:36 PM 0 7 0.00 0.20 0.00 0.00 0.20 0 ksoftirqd/0
03:59:36 PM 0 8 0.00 0.20 0.00 0.20 0.20 0 rcu_sched
03:59:36 PM 0 209 0.00 1.20 0.00 0.00 1.20 0 kworker/0:1H
03:59:36 PM 112 2047 0.20 0.20 0.00 0.00 0.40 1 beam.smp
03:59:36 PM 0 9976 0.00 0.20 0.00 0.00 0.20 1 supervisord
03:59:36 PM 0 9981 0.40 0.00 0.00 0.00 0.40 1 mongod
03:59:36 PM 0 10071 0.40 0.00 0.00 0.00 0.40 1 mongod
03:59:36 PM 0 10072 0.40 0.00 0.00 0.00 0.40 0 mongod
03:59:36 PM 0 10073 0.40 0.20 0.00 0.00 0.60 0 mongod
03:59:36 PM 0 10765 0.20 0.00 0.00 0.00 0.20 1 redis-server
03:59:36 PM 0 10766 0.20 0.20 0.00 0.60 0.40 0 redis-server
03:59:36 PM 0 10821 0.20 0.00 0.00 0.00 0.20 1 redis-server
03:59:36 PM 1000 27741 0.00 7.19 0.00 1.40 7.19 0 stress

可以看到%wait这一项高的还是stress进程。

大量等待调度的进程

stress可以模拟出处于运行态的进程,以下模拟出 10 个待调度的进程:

1
$ stress -c 10 --timeout 600

过两分钟后查看uptime查看平均负载:

1
2
$ uptime
16:09:20 up 51 days, 5:12, 2 users, load average: 9.38, 4.54, 2.35

平均负载又升高了,而且远高于以上两个实验,这也是当然的,根据平均负载的定义,这里有 10 个处于可运行的进程,那么平均负载的值肯定会接近 10。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
$ pidstat -u 5
Linux 4.15.0-96-generic (work) 06/07/2020 _x86_64_ (2 CPU)

04:11:59 PM UID PID %usr %system %guest %wait %CPU CPU Command
04:12:04 PM 0 8 0.00 0.20 0.00 0.20 0.20 0 rcu_sched
04:12:04 PM 0 9976 0.20 0.00 0.00 0.00 0.20 1 supervisord
04:12:04 PM 0 9981 0.60 0.00 0.00 0.00 0.60 1 mongod
04:12:04 PM 0 10071 0.20 0.00 0.00 0.00 0.20 1 mongod
04:12:04 PM 0 10072 0.20 0.00 0.00 0.00 0.20 0 mongod
04:12:04 PM 0 10073 0.20 0.00 0.00 0.00 0.20 0 mongod
04:12:04 PM 0 10260 0.40 0.20 0.00 0.00 0.60 1 mongod
04:12:04 PM 0 10261 0.40 0.00 0.00 0.00 0.40 1 mongod
04:12:04 PM 0 10262 0.20 0.00 0.00 0.00 0.20 1 mongod
04:12:04 PM 0 10763 0.20 0.00 0.00 0.20 0.20 1 redis-server
04:12:04 PM 0 10764 0.20 0.00 0.00 0.20 0.20 1 redis-server
04:12:04 PM 0 10766 0.20 0.20 0.00 0.80 0.40 0 redis-server
04:12:04 PM 0 10820 0.20 0.00 0.00 0.00 0.20 1 redis-server
04:12:04 PM 1000 28242 19.48 0.00 0.00 80.12 19.48 0 stress
04:12:04 PM 1000 28243 19.28 0.00 0.00 80.12 19.28 1 stress
04:12:04 PM 1000 28244 19.28 0.00 0.00 80.12 19.28 0 stress
04:12:04 PM 1000 28245 19.28 0.00 0.00 79.72 19.28 1 stress
04:12:04 PM 1000 28246 19.48 0.00 0.00 80.12 19.48 0 stress
04:12:04 PM 1000 28247 19.28 0.00 0.00 79.92 19.28 0 stress
04:12:04 PM 1000 28248 19.48 0.00 0.00 79.92 19.48 1 stress
04:12:04 PM 1000 28249 19.48 0.00 0.00 80.52 19.48 1 stress
04:12:04 PM 1000 28250 19.48 0.00 0.00 80.12 19.48 1 stress
04:12:04 PM 1000 28251 19.48 0.00 0.00 80.52 19.48 0 stress

可以看到有 10 个stress进程在抢 2 个 CPU。

小结

通过本文我们了解到:平均负载与 CPU 利用率没有必然联系。而当发现负载升高后,需要综合使用uptime, mpstat, pidstat等工具来分析是上述哪三个场景,从而找到负载升高的来源。

文章目录
  1. 1. 平均负载的定义
  2. 2. 进程的状态
    1. 2.1. 操作系统进程的基本状态
    2. 2.2. Linux 进程的基本状态
  3. 3. 理解定义
    1. 3.1. 平均负载与 CPU 利用率
  4. 4. 实验
    1. 4.1. 工具介绍
    2. 4.2. CPU 密集型
    3. 4.3. I/O 密集型
    4. 4.4. 大量等待调度的进程
  5. 5. 小结