﻿# mpstat —— 按 CPU 核心拆分利用率与中断分布

## 这个工具是做什么的

`sysstat` 套件成员。`top` 顶部的 `%Cpu(s)` 是**所有核的平均**，会把"单核跑满、其余空闲"平均成看似不高的占用。`mpstat` 能**逐核**看，专门回答"负载是均匀分布，还是压在某一个核上"，以及"中断/软中断集中在哪个核"。

## 可以回答什么问题

| 问题 | 怎么用 |
|------|--------|
| 所有核的负载是均匀的还是压在单核上？ | `mpstat -P ALL 1` 逐核对比 `%usr` |
| 软中断集中在哪个核？是不是 CPU0？ | `-P ALL` 看 `%soft` 列，集中在单核 = 网卡没开多队列 |
| 硬中断在哪个核处理？ | `-P ALL` 看 `%irq` 列 |
| 具体哪种中断最频繁？分布在哪些核？ | `mpstat -I CPU 1` 看每种中断的每秒次数和核分布 |
| 云主机 CPU 有没有被其他租户偷？ | `%steal` 非零 = 宿主超卖 |
| 中断风暴了没有？打在哪个核？ | `mpstat -I SUM 1` 看每核中断总次数 |

## mpstat 能观测到的所有指标

mpstat 提供**三种维度**的输出，分别回答三类问题：

### 维度一：CPU 利用率（默认模式 / `-P ALL`）

| 指标 | 含义 | 回答的问题 |
|------|------|-----------|
| `%usr` | 用户态 CPU 占用 | 业务代码在跑吗 |
| `%nice` | 被 nice 调过优先级的用户态 | 有低优先级任务在抢 CPU 吗 |
| `%sys` | 内核态占用（不含中断处理） | 系统调用/内核逻辑开销大吗 |
| `%iowait` | 空闲且有 IO 未完成 | IO 是瓶颈吗 |
| `%irq` | 硬中断处理占用 | 硬件中断在哪个核上跑 |
| `%soft` | 软中断处理占用 | 网络/块设备下半部在哪处理 |
| `%steal` | 被 hypervisor 偷走的时间 | 云主机被超卖了吗 |
| `%guest` | 运行 vCPU 的时间 | 宿主机上虚拟机跑了多久 |
| `%idle` | 真正的空闲 | 还有多少余量 |

### 维度二：中断汇总（`-I SUM`）

每个 CPU 核的**中断总次数/秒**，不区分中断号，适合快速定位"中断风暴打在哪个核上"。

### 维度三：中断明细（`-I CPU`）

**按中断号/中断名称**，逐核列出**每种中断的每秒次数**。这是 mpstat 最深层的观测能力——能精确到"IRQ 12 在 CPU0 上每秒触发 47 次"，适合分析：

- 哪个硬件设备中断最频繁
- 设备的 MSI/MSI-X 中断向量分布在哪些核上
- 内核 IPI（核间中断）频率是否过高（RES/CAL/TLB 异常高说明跨核调度或 TLB shootdown 过多）
- 本地定时器中断（LOC/s）频率，间接反映内核 CONFIG_HZ
- 硬件是否产生 MCE（机器检查异常）

## 数据来源

- **来源文件**：逐核 CPU 取自 `/proc/stat`（`cpu0`/`cpu1`… 每行各自的 jiffies 分类）；每核中断/软中断取自 `/proc/interrupts` 与 `/proc/softirqs`。
- **采集方式**：按间隔读两次这些文件，对每个核分别**做差**算出百分比或速率。
- **由此决定的特性**：`mpstat` 的逐核数据来自内核已经分好的 per-cpu 统计，所以能精确回答"负载压在哪个核""软中断集中在哪个核"——这是只看整机平均的 `top`/`vmstat` 给不出的。

```bash
yum install sysstat / apt install sysstat
```

## 一、用法

```bash
# --- CPU 利用率 ---
mpstat                   # 只输出一次开机以来的平均（参考意义小）
mpstat 1                 # 整机平均，每 1 秒刷新
mpstat 1 5               # 每秒一次，共 5 次
mpstat -P ALL 1          # 每个逻辑核分别一行 + 平均行（最常用）
mpstat -P 0 1            # 只看 0 号核
mpstat -P 0,3 1          # 只看 0、3 号核
# --- 中断统计 ---
mpstat -I SUM 1          # 每核的中断总次数/秒（不区分中断号）
mpstat -I CPU 1          # 每核按中断号/中断名称明细（最能定位中断问题）
mpstat -I ALL 1          # SUM + CPU 一起出
mpstat -I XALL 1         # 加软中断统计
mpstat -P ALL -I CPU 1   # CPU 利用率 + 中断明细同时输出
```

## 二、CPU 利用率输出样例

```bash
15:30:01  CPU   %usr  %nice  %sys %iowait  %irq  %soft %steal %guest %idle
15:30:02  all  12.53   0.00  3.14    0.25  0.00   0.50   0.00   0.00 83.58
15:30:02    0  99.00   0.00  1.00    0.00  0.00   0.00   0.00   0.00  0.00
15:30:02    1   1.00   0.00  0.00    0.00  0.00   0.00   0.00   0.00 99.00
15:30:02    2   0.99   0.00  0.99    0.00  0.00   0.00   0.00   0.00 98.02
15:30:02    3   2.00   0.00  1.00    1.00  0.00   2.00   0.00   0.00 94.00
```

`all` 行是平均（等于 top 看到的），下面每一行是一个逻辑核。上面这个例子：**0 号核 `%usr` 99% 被打满，其余核几乎空闲**——典型的单线程瓶颈。

## 三、CPU 利用率字段解读（含义同 top 的 %Cpu 行，但精确到每核）

| 字段 | 含义 | 判读 |
|------|------|------|
| `%usr` | 用户态 | 业务计算 |
| `%nice` | 被 nice 调过优先级的用户态 | |
| `%sys` | 内核态（**不含中断**，这点和 top 略有区别）| 系统调用、内核逻辑 |
| `%iowait` | 该核空闲且在等 IO | 高 = IO 瓶颈 |
| `%irq` | 硬中断占用 | 硬件中断处理 |
| `%soft` | **软中断** | 网络收发主要在这里，网卡风暴时某核爆高 |
| `%steal` | 被 hypervisor 偷走 | 云主机超卖时出现 |
| `%guest` | 运行 vCPU 的时间 | 宿主机才有 |
| `%idle` | 空闲 | 越低越忙 |

## 四、典型判断

- **单线程瓶颈**：`-P ALL` 下只有一个核 `%usr` 高、其余空闲 → 加核无用，得优化算法或改成多线程/多进程并行。
- **负载均匀**：各核 `%usr` 差不多 → 已经较好地利用了多核。
- **软中断集中在 CPU0**：`%soft` 只在 0 号核高 → 网卡没开多队列（RSS）或没配 RPS/RFS，网络中断全压一个核。解决：开网卡多队列、配置 `/proc/irq/*/smp_affinity` 或 RPS 把中断分散到多核。
- **某核 `%steal` 高**：云环境宿主超卖，同宿主的其他租户在抢 CPU，和你的代码无关。
- **`%iowait` 高但只集中某核**：该核上的进程在做同步 IO，转 `iostat`/`iotop` 查。

## 五、中断统计观测（`-I CPU` 深度分析）

`mpstat -I CPU 1` 是定位**中断分布问题**的利器。它能列出每种中断在每核上的每秒触发次数——从硬件 IRQ 到内核 IPI（核间中断）一览无余。

### 5.1 输出样例

以下是一台 4 核 CentOS 7（3.10 内核）的实际输出（仅截取两个周期的部分行）：

```bash
Linux 3.10.0-1160.108.1.el7.x86_64      07/12/26    _x86_64_    (4 CPU)
15:43:07     CPU        0/s    ...      12/s      24/s   ...    LOC/s    IWI/s    RES/s    CAL/s    TLB/s   ...
15:43:08       0       0.00    ...     47.00      0.00   ...   154.00     3.00    57.00     0.00     3.00   ...
15:43:08       1       0.00    ...      0.00      0.00   ...   175.00     1.00    87.00     8.00     4.00   ...
15:43:08       2       0.00    ...      0.00      0.00   ...   160.00     4.00   111.00     0.00     2.00   ...
15:43:08       3       0.00    ...      0.00      0.00   ...   158.00     1.00    34.00     0.00     2.00   ...
```

### 5.2 字段详解：所有中断列的含义

`-I CPU` 输出的列头分三类：**传统 IRQ 编号**、**MSI/MSI-X 中断号**、**命名中断**（内核 IPI 和特殊中断）。下面逐一解释。

#### 传统 IRQ 编号（0-15，8259 PIC 时代的遗产）

| 列头 | 对应中断 | 典型设备 | 说明 |
|------|---------|---------|------|
| `0/s` | IRQ 0 | 系统定时器（PIT/HPET） | 传统定时器，现代系统多用 LOC 替代，通常为 0 或极低 |
| `1/s` | IRQ 1 | 键盘控制器 | 物理机键盘中断，云主机通常为 0 |
| `4/s` | IRQ 4 | 串口 ttyS0 | 有串口设备才非零 |
| `6/s` | IRQ 6 | 软盘控制器 | 现代系统基本为 0 |
| `8/s` | IRQ 8 | RTC 实时时钟 | 周期性时钟，一般很低 |
| `9/s` | IRQ 9 | ACPI | 电源管理相关 |
| `11/s` | IRQ 11 | 其他设备 | 可能被 PCI 设备共享 |
| `12/s` | IRQ 12 | PS/2 鼠标（或 PCI 设备） | 云主机上如果非零，通常是 virtio 或存储控制器 |
| `14/s` | IRQ 14 | IDE 主通道 | 使用 IDE 磁盘时出现，现在少见 |
| `15/s` | IRQ 15 | IDE 从通道 | 同上 |

> 传统 IRQ 在现代系统上大部分为 0。如果某个编号持续非零，说明有设备没有用 MSI/MSI-X，路由到了传统中断线上——通常去找 `/proc/interrupts` 确认具体设备。

#### MSI/MSI-X 中断号（24 以上）

| 列头 | 含义 | 说明 |
|------|------|------|
| `24/s`, `25/s`, ... | PCI 设备的 MSI/MSI-X 中断向量 | 每个设备的中断向量号由内核动态分配，**具体对应哪个设备需查看 `/proc/interrupts`**。网卡多队列会分配多个连续向量号（每个队列一个） |

#### 命名中断（内核 IPI 和特殊中断）

这是 `-I CPU` 最有价值的部分——能看到**内核内部**的核间通信频率。

| 列头 | 全称 | 含义 | 判读 |
|------|------|------|------|
| **`NMI/s`** | Non-Maskable Interrupt | 不可屏蔽中断 | 硬件严重错误（内存 ECC、PCI 奇偶校验）时触发。**持续非零 = 硬件故障，需立即排查** |
| **`LOC/s`** | Local Timer Interrupts | **本地 APIC 定时器中断** | **这是每个核的调度时钟中断源**。频率由内核 `CONFIG_HZ` 决定（100/250/300/1000）。每个核每周期一次，所以约为 HZ 值。是现代系统上唯一的"规律性"中断 |
| **`SPU/s`** | Spurious Interrupt | 虚假中断 | 属于噪声，少量无害。**持续增高可能是硬件 APIC 问题** |
| **`PMI/s`** | Performance Monitoring Interrupt | 性能监控中断 | `perf`、`oprofile` 等工具使用 PMU 采样时触发。不用 perf 时为 0 |
| **`IWI/s`** | IRQ Work Interrupt | IRQ 工作队列中断 | 中断上下文中的部分工作需要延迟到进程上下文执行时，通过触发 IWI 来调度。**伴随网络/磁盘高 IO 时出现，正常现象** |
| **`RTR/s`** | — | 架构相关（x86 上通常为 0） | 非零时查阅具体内核版本的中断类型定义 |
| **`RES/s`** | Rescheduling Interrupts | **重新调度 IPI（核间中断）** | **跨核唤醒 task 时触发**。当 CPU A 上的进程唤醒 CPU B 上睡眠的任务时，A 向 B 发 RES IPI 让 B 立即检查是否需要重新调度。**值高 = 跨核唤醒频繁**，常见于多线程应用或负载均衡活跃时 |
| **`CAL/s`** | Function Call Interrupts | **函数调用 IPI** | 一个核要求另一个核执行某函数时触发（如 `smp_call_function()`）。**值高 = 跨核同步操作频繁**（如 RCU 回调、tlb flush 广播） |
| **`TLB/s`** | TLB Shootdown Interrupts | **TLB 刷新 IPI** | 进程切换时，若新旧进程不同 mm（不同地址空间），需要通知所有跑过该进程的核刷新 TLB。**多线程应用间值低（同 mm 不刷），频繁 fork/exec 或不同进程间切换多时值高** |
| **`TRM/s`** | Thermal Event Interrupts | 温度事件中断 | CPU 过热降频时触发。**非零 = 散热有问题** |
| **`THR/s`** | Threshold Interrupts | MCA 阈值中断 | 可纠正错误的累积计数达到阈值时触发。非零需结合 `mcelog` 分析 |
| **`DFR/s`** | Deferred Error Interrupts | 延迟错误中断 | 硬件错误延迟报告 |
| **`MCE/s`** | Machine Check Exception | 机器检查异常 | **致命硬件错误**（内存不可纠正错误、总线错误）。**非零必须立即停机排查** |
| **`MCP/s`** | Machine Check Poll | 机器检查轮询（已纠正） | 已纠正的硬件错误通过轮询上报。少量可接受，持续增多说明硬件劣化 |
| **`ERR/s`** | Error APIC Interrupt | APIC 错误中断 | APIC 内部错误 |
| **`MIS/s`** | Miscellaneous | 杂项中断 | 架构相关，通常为 0 |
| **`PIN/s`** | Posted Interrupt Notification | Posted 中断通知（虚拟化） | **虚拟化环境专用**：vCPU 被抢占时，hypervisor 通过 posted interrupt 通知 guest |
| **`NPI/s`** | Non-Posted Interrupt | 非 Posted 中断 | 虚拟化相关 |
| **`PIW/s`** | Posted Interrupt Wakeup | Posted 中断唤醒 | 虚拟化相关 |

### 5.3 对以上样例数据的逐项分析

| 指标 | 观测值 | 解读 |
|------|--------|------|
| **`LOC/s`** | 每核 100~216/s | **正常**。说明内核可能是 `CONFIG_HZ=100` 或 250，加上 nohz 关闭时额外 tick。4 核合计约 600~750 LOC/s，完全在正常范围 |
| **`RES/s`** | CPU0: 44~194, CPU1: 33~173, CPU2: 47~111, CPU3: 25~76 | **偏高**。RES 是跨核唤醒产生的 IPI。>100/s 说明跨核调度活跃——某核上的 IO 完成中断唤醒另一个核上睡眠的 task。也包含负载均衡的迁移通知。如果伴随 `%sys` 也高，需检查是否不必要的跨核竞争 |
| **`IWI/s`** | 每核 1~5/s | **正常**。IRQ 的延迟工作很轻 |
| **`TLB/s`** | CPU1: 2~8, CPU0: 0~6 | **正常低值**。说明当前运行的线程多数处于同一进程（同 mm），TLB 刷新不频繁。如果 >100/s 说明进程切换频繁且跨进程 |
| **`IRQ 12/s`** | 仅在 CPU0 上出现 6~47/s | **设备中断绑定在 CPU0 上**。可能是 virtio 存储控制器或网卡的单队列中断。需查看 `/proc/interrupts` 第 12 行确认设备。如果 `%irq` 也随之增高，建议配置 smp_affinity 或开启多队列把中断分散 |
| **`IRQ 24~30/s`** | 偶有 CPU2 上 5~10/s | PCI 设备 MSI 中断，频率很低，无关紧要 |
| **传统 IRQ** | 大部分为 0 | 正常。现代系统设备多数走 MSI/MSI-X |

> **关键判断**：这台机器的中断画像整体健康。LOC/s 是正常的调度时钟，RES/s 偏高但未到告警级别。唯一值得关注的是 IRQ 12 只绑在 CPU0——如果这个设备的 IO 量很大，可以考虑分散到其他核。

### 5.4 中断分析的典型判断

- **`LOC/s` 在所有核上稳定且相等** → 正常，这是调度时钟
- **`RES/s` 持续 >500/s** → 跨核唤醒过于频繁，检查是否有不必要的锁竞争或过多的线程间通知
- **`TLB/s` 持续 >200/s** → 大量跨进程切换（fork/exec 频繁），TLB 刷新开销不可忽略
- **某个 MSI 向量号只在 CPU0 上非零** → 设备中断没做分散，网络/存储密集时 CPU0 会成为瓶颈
- **`MCE/s` 或 `MCP/s` 非零** → 硬件告警，立即查 `dmesg` 和 `mcelog`
- **`PMI/s` 非零但没开 perf** → 有监控 agent 在后台采样，可能需要关闭或调整

## 六、常见排查方法与分析

### 6.1 软中断集中在 CPU0

**现象**：`mpstat -P ALL 1` 下 CPU0 的 `%soft` 很高，其余核接近 0。

**根因**：网卡未开多队列（RSS），或 RPS/RFS 未配置，所有网络中断的处理（softirq NET_RX）默认路由到 CPU0。

**排查链**：

```bash
mpstat -P ALL 1 → 确认 %soft 集中在单核
  → cat /proc/interrupts | grep <网卡名> → 看各队列中断是否分散到多核
  → ethtool -l <网卡> → 看 Combined/RX 队列数
    → 若只有 1 个队列 → ethtool -L <网卡> combined N（N ≤ CPU 核数）
    → 若不支持多队列 → 开启 RPS: echo <CPU掩码> > /sys/class/net/<iface>/queues/rx-0/rps_cpus
```

### 6.2 负载压在单核

**现象**：`-P ALL` 下只有一个核 `%usr` 接近 100%，其余核 `%idle` > 90%。

**结论**：单线程计算密集型任务，加 CPU 核数无意义。应并行化算法（多线程/多进程），或 `taskset` 绑核避免缓存变冷。

### 6.3 云主机 CPU 被偷

**现象**：`%steal` 持续 > 5%，且 `%usr + %sys` 不高但应用响应变慢。

**根因**：宿主机 CPU 超卖，hypervisor 把你的 vCPU 时间给了其他租户。

**处理**：向云平台提工单、迁移到不超卖的实例类型、或改用裸金属。

### 6.4 iowait 高

**现象**：某些核 `%iowait` 持续 > 20%。

**排查链**：

```bash
mpstat -P ALL 1 → 确认哪些核 iowait 高
  → iostat -x 1 → 确认哪块盘 await 高
    → iotop -o → 找读写进程
      → strace -e trace=read,write,fsync -p <PID> → 定位具体操作
```

### 6.5 中断风暴定位

**现象**：`%irq` 或 `%soft` 整体异常高，`vmstat 1` 的 `in` 列骤增。

**排查链**：

```bash
mpstat -I SUM 1 → 看哪几个核中断总数暴涨
  → mpstat -I CPU 1 → 看具体哪种中断/哪个 IRQ 号在暴涨
    → cat /proc/interrupts | grep <IRQ号> → 确认设备
      → 设备问题或流量爆增，针对性处理
```

## 七、交叉引用

- **整机CPU总览**：[top](/tools/cpu/top.md)（先看整机再看逐核）
- **进程/线程级**：[pidstat](/tools/cpu/pidstat.md)（定位到哪个进程/线程在烧核）
- **软中断深入**：[interrupts.md](/concepts/process/interrupts.md) §四（网卡收包完整流程）和 §五（softirq 机制）
- **中断亲和性**：[irq-affinity.md](/concepts/process/irq-affinity.md)（分散中断到多核）
- **磁盘 IO**：[iostat](/tools/disk/iostat.md)（iowait 高时进一步定位）
- **上下文切换**：[vmstat](/tools/cpu/vmstat.md)（看 cs 和 in 的整体趋势）
- **数据源**：[procfs](/tools/proc/procfs.md) §三（/proc/stat 和 /proc/interrupts）

## 八、结合本仓库

```bash
./cpu_demo &                 # 选场景 1（单线程用户态死循环）
mpstat -P ALL 1
# 预期：只有一个核 %usr 接近 100%，其余核 %idle 接近 100%
# —— 直观证明"这是单线程问题，多核帮不上忙"
```

对比：若把 cpu_demo 改成开多个 `busy_user_cpu` 线程，再看 `-P ALL`，多个核会同时上去。

## 七、和其他工具的分工

- `top` 按 `1` 也能逐核展开，但 `mpstat -P ALL 1` 输出是文本、可记录、含 `%irq`/`%soft` 更全，适合抓中断问题。
- 定位到"哪个核忙"后，用 `top -H`/`pidstat -t` 找是哪个线程跑在上面。

