
目录
[10.1 编译或运行](#10.1 编译或运行)
[14.1 验证P-state](#14.1 验证P-state)
[14.2 验证governor](#14.2 验证governor)
[14.3 验证Turbo](#14.3 验证Turbo)
[14.4 验证C-state](#14.4 验证C-state)
[14.5 验证热节流](#14.5 验证热节流)
[14.6 验证频率统计](#14.6 验证频率统计)
[14.7 验证容器视角](#14.7 验证容器视角)
[1. 建立实验矩阵](#1. 建立实验矩阵)
[2. 读结果时先看方向,再看大小](#2. 读结果时先看方向,再看大小)
[3. 故障注入一定要有停止条件](#3. 故障注入一定要有停止条件)
[4. 把结论写成可复查证据](#4. 把结论写成可复查证据)
CPU 利用率相同,性能未必相同:频率、睿频、温度、功耗限制和空闲状态退出延迟都会改变一段代码真正跑多久。
本文按"内核路径---可运行代码---观测结果---生产迁移"来写。涉及实时调度、大内存或高频跟踪的命令只在测试机执行;教程能运行很重要,不误伤业务更重要。
一、内核路径总览

任何指标都应放回某个阶段:对象是否创建、任务是否等待、完成由谁确认。找不到归属的数字通常还不能算证据。
2、P-state
描述性能与功耗工作点,现代硬件可能在内核提示与自主选择间协作。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
3、governor
performance、powersave、schedutil 等是策略接口,具体含义受驱动 intel_pstate 或 acpi-cpufreq 影响。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
4、Turbo
在温度、功耗和活跃核心允许时提高频率,不是固定长期承诺。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
5、C-state
CPU 空闲可进入更深状态节电,退出延迟通常随深度增加。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
6、热节流
温度或电气限制会降低频率,单看 governor 无法发现。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
7、频率统计
proc/cpuinfo 的 MHz 只是瞬时或估计视图,turbostat 与硬件计数更可靠。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
8、容器视角
容器通常不能独立控制物理频率,同机邻居和宿主策略会影响它。
这一层既要看静态配置,也要看运行时行为。配置只表达允许或偏好,真实路径还受负载、权限、内核版本与调用方式影响。排查时连续采样增量,并保存目标 PID、CPU、namespace 或设备身份。
验证时只改变一个变量,例如固定 CPU、调整输入规模或切换一个标志,再同时观察业务耗时、线程状态和内核事件。三者方向一致,结论才站得住。
九、对象关系图
快路径决定常态成本,慢路径决定尾延迟。实验要分别触发两者;平均 CPU 漂亮,不代表少数慢路径没有拖垮 P99。
十、动手实验:先读代码,再运行
对比空闲与单核负载的估计频率。虚拟机可能暴露固定或不可信 MHz,应以宿主工具为准。
cpp
#!/usr/bin/env bash
set -euo pipefail
for i in {1..10}; do
printf '%s ' "$(date +%T.%N)"
awk -F: '/cpu MHz/{sum+=$2;n++}END{printf "avg_mhz=%.0f\n",sum/n}' /proc/cpuinfo
sleep 1
done
10.1 编译或运行
cpp
chmod +x freq_watch.sh
./freq_watch.sh
stress-ng --cpu 1 --timeout 10s & ./freq_watch.sh
不要追求输出逐字一致。页大小、发行版补丁、虚拟化层与当前负载都会改变数字,我们关注改变条件后指标是否符合机制。
十一、配套观测命令
cpp
printf "time=%s kernel=%s\n" "$(date --iso-8601=seconds)" "$(uname -r)"
cpupower frequency-info 2>/dev/null
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver 2>/dev/null
turbostat --Summary --interval 1 2>/dev/null
grep . /sys/devices/system/cpu/cpu0/cpuidle/state*/{name,latency} 2>/dev/null
先保存基线,再启动实验,再执行同一组命令。累计字段必须做差分;权限不足要如实记录,不能解释成没有内核活动。
十二、从实验迁移到生产
生产采集要控制时间、频率与输出位置。先用 /proc、PSI 和统计量缩小范围,再上精细跟踪。
十三、故障分流图
先分运行与等待:计算热点要减少工作,等待问题要找到负责唤醒的人。把等待栈当 CPU 热点改,通常只会白忙。
14、逐项验证清单
14.1 验证P-state
记录P-state的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
14.2 验证governor
记录governor的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
14.3 验证Turbo
记录Turbo的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
14.4 验证C-state
记录C-state的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
14.5 验证热节流
记录热节流的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
14.6 验证频率统计
记录频率统计的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
14.7 验证容器视角
记录容器视角的对象身份、配置与计数,施加一个可撤销条件,观察完成速率、等待时间、错误与 SLO 是否同向变化。至少保留正常、异常、恢复三组原始数据;若无法重复,优先检查缓存冷热、并发干扰和统计口径。
15、常见误区与复盘
示例成功不代表生产可照抄。示例刻意缩小变量,生产还有 NUMA、容器限制、并发和设备固件。系统调用成功也只说明它承诺范围内的事情完成,不要自行扩大语义。
复盘把内容分为事实、推断和待验证项。事实指向原始输出,推断写出机制链,待验证项说明下次增加什么观测点。这样文章才不是看完觉得懂了,而是事故里真能拿来用。
16、写在最后
CPU 利用率相同,性能未必相同:频率、睿频、温度、功耗限制和空闲状态退出延迟都会改变一段代码真正跑多久。 把路径、代码和计数器放在一起后,这句话就不再像内核玄学。Linux 每一步都有对象、状态和完成条件,只是它们不在同一张监控图上。
先在安全环境复现,再用最小观测验证,最后小步修改并准备回滚。这才是技术教程应交付的东西:不仅告诉你是什么,还让你亲手看到它为什么。
追加实验:用对照组验证而不是凭感觉
第一个实验负责看见机制,第二个实验应该负责推翻错误直觉。很多教程只展示成功路径,读者照着跑出结果以后仍不知道异常长什么样。下面补一套对照流程,让数据自己说话。
本次对象是"Linux CPU频率为何忽高忽低:cpufreq、idle状态与性能抖动教程"。先创建独立工作目录,把内核版本、启动参数、进程限制、CPU 列表和当前 PSI 保存下来。测试期间不要同时升级软件、修改多个 sysctl 或运行别的压测,否则即使结果变化,也无法说明是哪一个条件造成的。虚拟机最好记录 vCPU、内存和磁盘后端,容器则额外保存 cgroup 路径与 namespace 标识。
cpp
#!/usr/bin/env bash
set -euo pipefail
name=${1:-linux-lab}
out=${2:-"$name-$(date +%Y%m%d-%H%M%S)"}
mkdir -p "$out"
{
date --iso-8601=ns
uname -a
cat /proc/cmdline
grep -E 'Cpus_allowed_list|Mems_allowed_list|Seccomp|NoNewPrivs' /proc/self/status
cat /proc/pressure/{cpu,memory,io}
} >"$out/environment.txt"
sample() {
local phase=$1
{
printf '\n=== %s phase=%s ===\n' "$(date --iso-8601=ns)" "$phase"
vmstat 1 2 | tail -n 1
cat /proc/pressure/{cpu,memory,io}
grep -E 'ctxt|processes|procs_running|procs_blocked|pgfault|pgmajfault' /proc/stat /proc/vmstat 2>/dev/null || true
} >>"$out/samples.log"
}
for phase in baseline baseline baseline; do sample "$phase"; done
echo "现在在另一个终端运行文章实验,然后按 Enter"
read -r
for phase in load load load; do sample "$phase"; done
echo "解除实验条件后按 Enter"
read -r
for phase in recovery recovery recovery; do sample "$phase"; done
printf 'results saved in %s\n' "$out"
这份脚本故意不替你自动施压,因为不同主题的安全边界不同:实时调度不能和网络丢包用同一种注入方式,文件系统实验更不能随手对真实数据盘断电。它只负责保存公共基线。运行文章前面的专用程序时,再把对应的 /proc/PID、设备队列或协议统计追加到 sample 函数。这样公共指标和主题指标会共用时间戳,后面不必靠肉眼拼图。
1. 建立实验矩阵
至少运行四组:默认配置加小负载、默认配置加大负载、改变单一条件后的小负载、改变单一条件后的大负载。每组执行三次以上,首次结果单独标记,因为动态链接、Page Cache、JIT、CPU 频率和内存首次触碰都可能让第一轮特殊。若三次差异很大,先延长运行时间或隔离干扰,不要急着取平均掩盖波动。
+----------------+------------+------------+----------------------+
| 组别 | 唯一变量 | 业务指标 | 内核证据 |
+----------------+------------+------------+----------------------+
| A 基线 | 默认 | 吞吐/P99 | 队列、fault、切换 |
| B 压力 | 仅加负载 | 吞吐/P99 | 压力指标增量 |
| C 对照 | 仅改一项 | 吞吐/P99 | 目标路径是否改变 |
| D 恢复 | 撤销改动 | 是否复原 | 积压是否停止增长 |
+----------------+------------+------------+----------------------+
2. 读结果时先看方向,再看大小
假设"Linux CPU频率为何忽高忽低:cpufreq、idle状态与性能抖动教程"中的某条慢路径是根因,那么施压时不仅业务 P99 应变差,对应等待、队列或事件也应增长;撤销条件后,新增事件速率应下降。若只有业务变慢、内核证据没动,可能观察错了边界;若内核计数猛增、业务毫无影响,它更可能只是背景工作。相关性还不等于因果,但连相关性都没有,就不该急着改生产参数。
累计计数必须换算为单位时间增量。容器内部和宿主机统计还可能覆盖不同对象,记录 PID 的宿主视图、cgroup 路径和 net/mount namespace。CPU 指标至少展开到每颗逻辑 CPU;多队列设备则展开到队列。平均值经常把最需要处理的局部热点磨平。
3. 故障注入一定要有停止条件
在运行前写清最大持续时间、最大内存、最大 CPU、允许产生的临时文件和恢复命令。终端断开时也要能自动停止,所以脚本应使用 trap 清理后台任务。涉及 mount、nftables、实时策略、模块和设备绑定时,最好在一次性虚拟机或新建 namespace 中操作。kill -9 不是通用清理方案:它无法撤销内核配置,也可能留下挂载、规则和子进程。
4. 把结论写成可复查证据
一份合格记录至少包含原始命令、标准输出和错误输出、退出码、精确时间、内核版本与修改前后值。结论后面附一句反证条件,例如"如果换成另一种文件系统仍同样发生,则当前解释不足"。这种写法没有口号那么痛快,却能让半年后的同事知道结论适用于什么边界。
最后别忘了恢复验证。配置恢复原值不代表系统已经恢复:Page Cache 可能仍是冷的,TCP 连接还在重建,异步回调可能尚未排空,CPU 频率也需要时间回升。继续采样一个完整业务周期,确认错误停止增长、队列回落且 SLO 恢复,实验才算真正结束。