跳到主要内容

深入理解 Linux 平均负载:远不止 CPU 使用率

当你在 Linux 系统上执行 uptime 命令时,会看到类似下面的输出,它提供了系统状态的快照:

$ uptime
02:34:03 up 2 days, 20:14, 1 user, load average: 0.63, 0.83, 0.88

让我们解析一下这行信息:

  • 02:34:03: 当前系统时间。

  • up 2 days, 20:14: 系统已运行时间。

  • 1 user: 当前登录的用户数。

load average: 0.63, 0.83, 0.88: 分别是过去 1 分钟、5 分钟、15 分钟的系统平均负载。

最后这三个数字——“平均负载”(Load Average)——是衡量系统性能的关键指标,但常常被误解。下面我们来深入探讨它的真正含义。

1. 什么是平均负载?

一个常见的误区是认为平均负载直接等同于 CPU 使用率。例如,负载 0.63 并不简单地意味着 CPU 占用率为 63%。你可以通过 man uptime 命令查阅官方的详细解释。

简单来说,平均负载是指在特定时间间隔内,系统中处于“可运行状态”和“不可中断状态”的平均进程数。 它本质上是活跃(需要 CPU 或 等待 I/O)进程的平均数量,与 CPU 使用率没有直接的一一对应关系。

我们来明确一下这两种状态:

可运行状态(Runnable State, R): 指进程要么正在使用 CPU,要么已经准备就绪、等待 CPU 调度。在 ps 命令的输出中,它们通常显示为 R 状态。

不可中断状态(Uninterruptible Sleep State, D): 指进程正在执行内核关键流程(通常是等待硬件 I/O 响应,如磁盘读写、某些网络操作),并且不能被信号中断。在 ps 命令输出中,它们显示为 D 状态(有时被称为 Disk Sleep)。

为什么 D 状态很重要?想象一个进程正在向磁盘写入关键数据。为了保证数据一致性,系统不允许在磁盘确认写入完成前中断这个进程。这种状态是对进程和硬件的一种保护机制。如果大量进程阻塞在 D 状态,即使 CPU 本身很空闲,系统也会显得非常缓慢,因为这些进程都在等待 I/O。

因此,我们可以理解为:平均负载 ≈ 平均(正在运行的进程数 + 等待 CPU 的进程数 + 等待 I/O 的进程数)。

理解平均负载与 CPU 核心数的关系至关重要:

在一个 2 核 CPU 系统上,平均负载为 2.00 意味着:平均来看,所有 CPU 核心都被“占满”了(要么在运行进程,要么有进程在排队等待)。

在一个 4 核 CPU 系统上,平均负载为 2.00 意味着:平均来看,CPU 有 50% 的空闲时间。

在一个 1 核 CPU 系统上,平均负载为 2.00 意味着:平均来看,有 1 个进程在运行,同时还有 1 个进程在等待 CPU。

2. 如何分析平均负载?

分析平均负载通常遵循以下步骤:

2.1 明确系统 CPU 核心数

评估负载高低的前提是知道系统有多少个逻辑 CPU 核心。负载值是相对于核心数而言的。你可以通过 top 命令(运行时按 1)或查询 /proc/cpuinfo 文件来获取:

# 统计逻辑处理器的数量
$ grep 'processor' /proc/cpuinfo | wc -l
2

注意:grep 'model name' 显示的是 CPU 型号,grep 'processor' 包含超线程在内的逻辑核心数)

知道了 CPU 核心数(假设为 N),如果平均负载持续超过 N,就表明系统处于过载状态。

2.2 解读三个负载数值(1 分钟、5 分钟、15 分钟)

这三个数值反映了负载的变化趋势:

  • 数值接近(例如 2.10, 2.05, 2.00): 系统负载非常平稳。

  • 1 分钟 < 5 分钟 < 15 分钟(例如 1.50, 2.50, 3.00): 负载在最近呈下降趋势。

  • 1 分钟 > 5 分钟 > 15 分钟(例如 3.00, 2.00, 1.50): 负载在最近呈上升趋势。这种情况需要密切关注。一旦 1 分钟负载接近或超过 CPU 核心数,就需要立即排查问题。

例子: 在一个单核 CPU 系统上,负载为 1.73, 0.60, 7.98 表示:

  • 过去 1 分钟:平均有 73% 的超载(1 个进程运行,0.73 个进程等待)。

  • 过去 5 分钟:平均有 40% 的空闲(负载 0.60)。

  • 过去 15 分钟:平均有 698% 的超载(1 个进程运行,约 7 个进程等待)。

趋势:负载曾经非常高,但正在迅速下降。

2.3 何时需要关注高负载?

一个常用的经验法则是:当平均负载持续超过 CPU 核心数的 70% 时(例如,2 核 CPU 负载持续高于 1.4),就应该开始排查。过高的负载会导致进程响应缓慢,影响服务。

但是,70% 并非绝对标准。最佳实践是:

  1. 建立基线: 监控系统在正常运行状态下的平均负载。
  2. 关注趋势: 相对于基线,负载是否出现显著增长(例如翻倍)通常比是否超过某个固定阈值更重要。

3. 平均负载与 CPU 使用率的关键区别

再次强调核心区别:

  1. 平均负载: 统计处于 可运行状态(R) 和 不可中断状态(D) 的进程数量。高负载意味着有许多进程处于活跃或等待状态(等待 CPU 或等待 I/O)。
  2. CPU 使用率: 衡量 CPU 时间用于执行指令的百分比。高 CPU 使用率会 导致 高负载,但高负载 不一定 意味着高 CPU使用率。

思考以下场景:

CPU 密集型: 进程进行大量计算,消耗大量 CPU 时间。这会同时推高 CPU 使用率和平均负载。(R 状态 - 运行中)

I/O 密集型: 进程等待缓慢的磁盘或网络,不直接消耗 CPU,但处于 D 状态。这会推高平均负载,但此时 CPU 使用率(%usr, %sys)可能很低,而 %iowait 可能很高。(D 状态)

大量就绪进程: 许多进程准备运行但需等待 CPU 调度(例如,进程数远超核心数)。这会推高平均负载,CPU 使用率通常也会很高,因为调度器会让 CPU 保持忙碌。(R 状态 - 等待中)

4. 案例分析:诊断高负载

下面通过几个实际案例,演示不同类型的问题如何影响平均负载,以及如何使用常用工具定位根源。

实验环境:

  • 操作系统:Ubuntu 18.04 (或类似 Linux 发行版)

  • 配置:2 核 CPU,8GB 内存 (请根据你的环境调整命令)

  • 工具:预先安装 stress (或 stress-ng) 和 sysstat

sudo apt update
sudo apt install stress sysstat stress-ng -y

stress/stress-ng: 用于产生各种系统压力的工具。

sysstat: 提供 mpstat (CPU 统计) 和 pidstat (进程统计) 等性能工具。

我们将使用多个终端连接到同一台 Linux 机器。在每个测试开始前,记录初始负载:

$ uptime
..., load average: 0.11, 0.15, 0.09 # 示例初始负载

4.1 场景一:单个 CPU 密集型进程

模拟一个进程占满单个 CPU 核心。

终端 1 (产生负载):

# 对 1 个 CPU 核心施加压力,持续 600 秒
$ stress --cpu 1 --timeout 600

终端 2 (监控负载):

# 动态观察 uptime 输出,-d 高亮变化
$ watch -d uptime
# 预期:1 分钟平均负载逐渐接近 1.00
# ..., load average: 1.00, 0.75, 0.39

终端 3 (分析 CPU 使用率):

# 监控所有 CPU (-P ALL),每 5 秒报告一次
$ mpstat -P ALL 5 1
# 预期:看到一个 CPU 的用户态使用率 (%usr) 接近 100%,%iowait 很低
# 13:30:11 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
# 13:30:11 all 50.05 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 49.95
# 13:30:11 0 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 100.00
# 13:30:11 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

终端 3 (定位进程):

# 查看进程的 CPU 使用情况 (-u),每 5 秒报告一次,共 1 次
$ pidstat -u 5 1
# 预期:stress 进程的 %CPU 接近 100%
# 13:37:12 UID PID %usr %system %guest %wait %CPU CPU Command
# 13:37:12 0

结论: 平均负载升高到 1.00,原因是 CPU 密集型进程 (stress) 完全占用了 1 个 CPU 核心 (%usr=100%)。负载直接反映了活跃的 CPU 密集型进程数量。

4.2 场景二:CPU 核心完全饱和

模拟 CPU 密集型进程数刚好等于 CPU 核心数。

终端 1 (产生负载):

# 在 2 核 CPU 系统上运行 2 个 CPU 密集型工作进程,持续 600 秒
$ stress --cpu 2 --timeout 600

终端 2 (监控负载):

$ watch -d uptime
# 预期:1 分钟平均负载逐渐接近 2.00 (等于 CPU 核心数)
# ..., load average: 2.00, 1.55, 0.80

终端 3 (分析 CPU 使用率):

$ mpstat -P ALL 5 1
# 预期:所有 CPU 核心都接近 100% 繁忙 (%idle 接近 0),主要是 %usr。%iowait 低。
# 14:20:10 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
# 14:20:15 all 99.90 0.00 0.10 0.00 0.00 0.00 0.00 0.00 0.00 0.00 # 所有 CPU 都被占满
# 14:20:15 0 99.80 0.00 0.20 0.00 0.00 0.00 0.00 0.00 0.00 0.00
# 14:20:15 1 100.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00 0.00

终端 3 (定位进程):

$ pidstat -u 5 1
# 预期:2 个 stress 进程,每个进程大约占用 100% 的 CPU (%CPU ≈ 100)。%wait 应该很低。
# 14:21:05 UID PID %usr %system %guest %wait %CPU CPU Command
# 14:21:10 0 3100 100.00 0.00 0.00 0.20 100.00 0 stress # %wait 低
# 14:21:10 0 3101 100.00 0.00 0.00 0.20 100.00 1 stress # %wait 低

结论: 平均负载达到 2.00 (等于核心数),表明所有 CPU 核心都被充分利用。系统处于饱和状态,但由于进程数没有超过核心数,等待 CPU 的时间 (%wait) 很短。

4.3 场景三:大量进程竞争 CPU (CPU 过载)

模拟运行的 CPU 密集型进程数远超 CPU 核心数。

终端 1 (产生负载):

# 在 2 核 CPU 系统上运行 8 个 CPU 密集型工作进程,持续 600 秒
$ stress -c 8 --timeout 600

终端 2 (监控负载):

$ watch -d uptime
# 预期:平均负载显著高于 CPU 核心数,逐渐接近 8.00
# ..., load average: 7.97, 5.93, 3.02

终端 3 (分析 CPU 使用率):

$ mpstat -P ALL 5 1
# 预期:所有 CPU 核心都接近 100% 繁忙 (%idle 接近 0),主要是 %usr。%iowait 低。
# (与场景 4.2 的 mpstat 输出类似,CPU 看起来都是 100% 忙)

终端 3 (定位进程):

$ pidstat -u 5 1
# 预期:8 个 stress 进程,每个进程获得的 CPU 份额约为 25% (100% * 2 CPU / 8 进程)。%wait 会非常高,表示大量时间在等待 CPU。
# 14:23:30 UID PID %usr %system %guest %wait %CPU CPU Command
# 14:23:30 0 3190 25.00 0.00 0.00 74.80 25.00 0 stress # 高 %wait
# 14:23:30 0 3191 25.00 0.00 0.00 75.20 25.00 0 stress # 高 %wait
# ... (另外 6 个 stress 进程,状态类似) ...

结论: 在 2 核 CPU 上平均负载高达 8.00,表明系统严重过载。有 8 个可运行进程在竞争 2 个 CPU 核心。pidstat 显示每个进程等待 CPU 的时间(%wait)非常高,这是 CPU 资源不足的典型特征。

4.4 场景四:磁盘 I/O 密集型进程

模拟进程因等待磁盘 I/O 而导致负载升高。

终端 1 (产生负载):

# 运行 1 个 I/O worker,不停执行 sync 操作(强制缓存刷新到磁盘),持续 600 秒
$ stress -i 1 --timeout 600

终端 2 (监控负载):

$ watch -d uptime
# 预期:1 分钟平均负载逐渐升高,可能超过 1.00 (取决于 sync 的阻塞程度)
# ..., load average: 1.06, 0.58, 0.37

终端 3 (分析 CPU 使用率):

$ mpstat -P ALL 5 1
# 预期:一个或多个 CPU 的 %iowait 显著升高,可能伴随一些 %sys (系统调用开销),%usr 和 %idle 相对较低。
# 13:41:33 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
# 13:41:33 all 0.21 0.00 12.07 32.67 0.00 0.21 0.00 0.00 0.00 54.84
# 13:41:33 0 0.43 0.00 23.87 67.53 0.00 0.43 0.00 0.00 0.00 7.74 # 高 %iowait 和 %sys
# 13:41:33 1 0.00 0.00 0.81 0.20 0.00 0.00 0.00 0.00 0.00 98.99

终端 3 (定位进程 - I/O 视角):

# 查看进程的磁盘 I/O 统计 (-d),每 5 秒报告一次
$ pidstat -d 5 1
# 预期:stress 进程显示出显著的磁盘写入活动 (kB_wr/s)。
# 13:43:01 UID PID kB_rd/s kB_wr/s kB_ccwr/s iodelay Command
# 13:43:06 0 2997 0.00 8483.20 0.00 0 stress # 高写入量

结论: 平均负载升高到 1.06,主要是因为 stress 进程频繁执行 sync 操作,导致其进入不可中断睡眠状态(D 状态)等待磁盘 I/O 完成。mpstat 中的高 %iowait 和 pidstat -d 中的高写入量证实了这一点。

4.5 场景五:大量短时进程(模拟 Fork Bomb 效果)

模拟快速、大量地创建和销毁进程,考验系统的进程管理能力。注意: 真正的 Fork Bomb (:(){ :|:& };:) 可能导致系统完全无响应,这里使用 stress-ng 进行可控模拟。

终端 1 (产生负载):

# 启动 8 个 worker,每个 worker 不断 fork 子进程,持续 120 秒 (如果系统较弱,减少 --fork 数)
$ stress-ng --fork 8 --timeout 120

终端 2 (监控负载):

$ watch -d uptime
# 预期:平均负载急剧飙升
# ..., load average: 15.50, 8.10, 3.50

终端 3 (分析 CPU 使用率):

$ mpstat -P ALL 2 1
# 预期:系统态 CPU 使用率 (%sys) 非常高,因为内核忙于创建/销毁进程。%usr 可能也高(如果子进程执行了操作)。
# 16:05:05 CPU %usr %nice %sys %iowait %irq %soft %steal %guest %gnice %idle
# 16:05:07 all 10.00 0.00 85.00 0.00 0.00 0.50 0.00 0.00 0.00 4.50 # 极高的 %sys

终端 3 (观察进程情况):

# 统计总进程数 (会快速变化)
$ ps aux | wc -l
# 预期: 一个较高的、不断变化的数字
# ~350

# 查看进程活动 (CPU 可能被分散,或者系统时间占主导)
$ pidstat -u 5 1
# 预期:看到大量短暂的 'stress-ng' 子进程出现又消失,整体 %system 可能很高。
# (输出会非常动态)

结论: 极高的平均负载是由于短时间内创建了海量进程。系统在进程管理上不堪重负,导致内核态 CPU 使用率(%sys)激增,大量进程可能在可运行队列(R 状态)中等待调度器的处理。

5. 总结:融会贯通

理解 Linux 平均负载对于性能监控至关重要。请记住以下关键点:

  • 负载不等于 CPU 使用率: 它反映的是处于 可运行(R) 和 不可中断(D) 状态的进程平均数。

  • 核心数是关键: 负载需要结合 CPU 核心数 和 历史基线 来解读。趋势变化往往比绝对值更重要。

  • 高负载成因多样: 可能源于 CPU 计算密集、磁盘 I/O 瓶颈、网络 I/O 等待,或是过度的进程创建等。

  • 诊断需对症下药: uptime 提供概览。要定位根源,需结合使用:

    • mpstat: 分析各 CPU 核心的使用情况 (%usr, %sys, %iowait)。

    • pidstat -u: 定位消耗 CPU 的进程 (%CPU, %usr, %sys) 和等待 CPU 的进程 (%wait)。

    • pidstat -d: 定位 I/O 密集型进程 (kB_rd/s, kB_wr/s)。

    • iostat: 查看详细的磁盘 I/O 统计。

    • vmstat: 查看系统范围的 I/O、内存、进程、CPU 活动。

    • top/htop: 提供交互式的进程概览。

通过正确解读平均负载,并运用合适的工具深入分析负载升高的原因,你就能更有效地诊断和解决 Linux 系统的性能瓶颈。