Linux CPU Hotplug 与 cpufreq 热管理深度实战:从热插拔拓扑到温控调频
一、为什么 CPU 拓扑感知决定系统性能上限
在现代多核异构平台上,物理 CPU 并非一成不变地工作——笔记本合盖时核心可能被关闭,服务器过热时系统需要主动关核,手机大小核之间动态迁移任务。这一切的背后是 Linux 内核的 CPU hotplug(热插拔)子系统和 cpufreq(CPU 频率调节)子系统的紧密协作。
理解这两个子系统的交互,是从"能用"到"用好"多核系统的关键一步。本文将从硬件拓扑出发,完整拆解 CPU 热插拔的状态机、内核同步机制、cpufreq 调度器的 governor 算法、热压力反馈闭环,最后给出可调参数速查表与生产环境调优案例。
二、CPU 拓扑与热插拔基础
2.1 拓扑层级:DIE → PACKAGE → CORE → THREAD
Linux 用四层层级描述 CPU 拓扑:
NUMA Node (Socket / Package)
└── DIE
└── Physical Core (package_id)
└── Thread (sibling, SMT超线程)
关键 sysfs 路径:
| 路径 | 含义 |
|---|---|
/sys/devices/system/cpu/cpuN/topology/physical_package_id |
所属物理封装(Socket) |
/sys/devices/system/cpu/cpuN/topology/core_id |
封装内核心编号 |
/sys/devices/system/cpu/cpuN/topology/thread_siblings_list |
同核超线程兄弟 |
/sys/devices/system/cpu/cpuN/cache/indexN/level |
L1/L2/L3 缓存层级 |
2.2 CPU 状态机
每个逻辑 CPU 在 hotplug 框架下经历以下状态:
CPU_OFFLINE ──up──→ CPUHP_OFFLINE ──→ CPUHP_AP_ONLINE_DYN ──→ CPU_ONLINE
│
CPU_OFFLINE ←──down── CPUHP_TEARDOWN_GENERIC ←── CPUHP_ONLINE ←───┘
实际的热插拔操作通过 sysfs 接口触发:
# 关闭核心 3
echo 0 > /sys/devices/system/cpu/cpu3/online
# 重新开启核心 3
echo 1 > /sys/devices/system/cpu/cpu3/online
# 查看状态
cat /sys/devices/system/cpu/cpu3/online # 0 或 1
cat /sys/devices/system/cpu/online # 当前在线核心列表
cat /sys/devices/system/cpu/possible # 系统支持的核心列表
cat /sys/devices/system/cpu/present # 已交付给OS的核心列表
2.3 关键概念辨析
- possible:固件(ACPI/Device Tree)报告给内核的硬件能力上限,静态不变
- present:已通过 reset 交付给 OS 的核心,但尚未启动
- online:已完成启动、可被调度器使用的核心
一个核心必须从 present → online 才能被 scheduler 分配到任务。
三、CPU 热插拔的同步屏障机制
3.1 cpuhp 状态回调注册
内核通过 cpu_setup_state() 注册热插拔回调,回调在 states 链表的关键节点执行:
#include <linux/cpuhp.h>
static int my_ctor(unsigned int cpu)
{
/* 在核心启动到 online 之前执行 */
struct my_dev *dev = per_cpu(my_dev, cpu);
dev->cache_buf = kzalloc(CACHE_SZ, GFP_KERNEL);
return 0;
}
static int my_dtor(unsigned int cpu)
{
/* 在核心关闭之前执行 */
struct my_dev *dev = per_cpu(my_dev, cpu);
kfree(dev->cache_buf);
return 0;
}
static enum cpuhp_state hp;
static int __init my_init(void)
{
/* 注册回调,在 CPUHP_AP_ONLINE_DYN 阶段执行 */
hp = cpuhp_setup_state(CPUHP_AP_ONLINE_DYN, "mydrv:online", my_ctor, my_dtor);
return 0;
}
3.2 读侧保护:get_online_cpus() / put_online_cpus()
这是热插拔的 RCU-like 同步原语,使用 per-CPU 引用计数 + 互斥锁保护临界区:
/* 遍历所有在线 CPU,不会被热插拔打断 */
get_online_cpus();
for_each_online_cpu(cpu) {
do_something_on(cpu);
}
put_online_cpus();
内部实现使用 cpuhp_rwsem(读写信号量),down 时等待正在进行的热插拔操作完成。get_online_cpus() 本身是 preemptible 的,但不能在已有 spinlock 的上下文中使用。
3.3 CPUHP 通知链
内核子系统通过 cpu_notifier 注册热插拔事件通知:
static int my_cpu_notifier(struct notifier_block *nb, unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch (action) {
case CPU_ONLINE:
/* 核心上线 */
break;
case CPU_DEAD:
/* 核心离线(已停止执行) */
break;
case CPU_DYING:
/* 核心正在死亡(可迁移线程) */
break;
}
return NOTIFY_OK;
}
static struct notifier_block my_nb = {
.notifier_call = my_cpu_notifier,
};
register_cpu_notifier(&my_nb);
四、cpufreq 架构与 Governor 算法详解
4.1 三层架构
Scheduler Layer (schedutil governor reads runqueue load)
↓
cpufreq Core (频率选择决策)
↓
cpufreq Driver (硬件接口:ACPI cppc / intel_pstate / amd-pstate)
4.2 六大 Governor 算法对比
| Governor | 策略 | 适用场景 | 特点 |
|---|---|---|---|
| performance | 恒定最高频 | 低延迟关键任务 | 无动态调频,功耗最高 |
| powersave | 恒定最低频 | 节能模式 | 响应延迟最大 |
| ondemand | 负载>阈值→升频 | 通用服务器 | 过时,已被 schedutil 取代 |
| conservative | 渐进升频 | 嵌入式 | 温和调频但响应慢 |
| schedutil | 调度器反馈直接调频 | 现代桌面/服务器 | 与 scheduler 紧密耦合,响应最快 |
| userspace | 用户态手动设置 | 调试/特殊需求 | 需外部控制程序 |
4.3 schedutil 深度解析
schedutil 是 Linux 4.7 后引入的最先进 governor,它的核心创新在于直接使用调度器的 CPU 利用率数据,而非独立采样:
// kernel/sched/cpufreq_schedutil.c
static void sugov_update_single(struct update_util_data *hdl,
unsigned long util, unsigned long max)
{
struct sugov_policy *sg_cpu = container_of(hdl, ...);
unsigned long freq_next;
// 直接使用调度器提供的负载数据
freq_next = sg_cpu->funcs->get_next_freq(sg_cpu, util, max);
cpufreq_driver_fast_switch(sg_cpu->policy, freq_next);
}
关键工作流程:
- CFS runqueue 通过
cpufreq_update_util()在每次任务入队/出队时调用回调 - 回调记录当前 CPU 利用率(
sched_cpu_util()) - 调度器 tick 或 idle 入口时触发频率计算
- 计算使用
freq = max_freq * (util / max_cap)公式 - 通过 fast switch(直接写寄存器 IPI)完成调频,延迟约 10-20μs
4.4 调频策略配置
# 查看可用 governor
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# 设置 governor
echo "schedutil" > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# 查看当前频率
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# 查看频率范围
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_min_freq
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
# 手动限制频率(如限制最大到 2GHz)
echo 2000000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_max_freq
# 查看可用频率列表
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
4.5 Energy Model 与能效感知调度
ARM/EAS(Energy Aware Scheduling)通过 Energy Model 描述每颗 CPU 在不同频率下的功耗:
// EM 表结构(简化)
struct em_perf_state {
unsigned long frequency; // kHz
unsigned long power; // mW
unsigned long cost; // energy efficiency metric
};
struct em_perf_domain {
struct em_perf_state *table; // OPP 表
unsigned int nr_perf_states;
};
调度器根据 task 的 CPU util 和能效表,将轻任务放在低频小核、重任务放在高频大核。
五、温控子系统与热管理闭环
5.1 Thermal 框架架构
Thermal Zone Device (温度传感器)
↓ 采样温度
Thermal Governor (决定冷却策略)
↓ 输出冷却需求
Cooling Device (执行冷却:cpufreq / fan / hotplug)
5.2 Fair Share Governor
最通用的温控 governor,按权重分配热预算:
# 查看 thermal zone 信息
cat /sys/class/thermal/thermal_zone0/type # 如 x86_pkg_temp
cat /sys/class/thermal/thermal_zone0/temp # 当前温度(m°C)
cat /sys/class/thermal/thermal_zone0/passive # 被动冷却延迟(ms)
# 查看冷却设备
cat /sys/class/thermal/cooling_device0/type # 如 Processor
cat /sys/class/thermal/cooling_device0/cur_state # 当前状态
cat /sys/class/thermal/cooling_device0/max_state # 最大状态
5.3 Step Wise Governor
阶跃式温控算法,将温度区间映射到冷却级别:
Temp Range: 0-60°C 60-70°C 70-80°C 80°C+
Cooling Level: No/throttle Level 1 Level 2 Max temp → emergency
5.4 Power Allocator Governor
基于 PID 控制器的先进温控算法,精确控制 thermal zone 温度趋近目标值:
PID Input: target_temp - current_temp → error
PID Output: power budget → cpufreq power limit
这是现代手机平台(Qualcomm、MTK)的标准配置。
5.5 热插拔与热管理的联动
Hotplug governor 直接根据温度关闭核心:
# Android 平台典型的 thermal engine 配置示例
# 当温度超过阈值,逐个关闭大核
thermal-engine.conf:
[SSD]
device=/sys/devices/system/cpu/cpu6/online
trigger=85 # 85°C 触发关闭
clear=75 # 75°C 以下恢复开启
cat /sys/devices/system/cpu/cpu6/online # 查看是否被温控关闭
# Intel DPTF 热插拔控制
cat /sys/devices/virtual/thermal/thermal_zone0/mode # enabled/disabled
六、手动调频与温度监控实战
6.1 查看温度传感器
# 安装 lm_sensors
sensors # 输出所有传感器数据
# 通过 sysfs 读取
cat /sys/class/hwmon/hwmon0/temp1_input # CPU 温度(m°C)
# 查看 thermal 子系统所有区域
for z in /sys/class/thermal/thermal_zone*; do
echo "$(cat $z/type): $(cat $z/temp)°C"
done
6.2 CPU 调频调优脚本
#!/bin/bash
# performance_boost.sh - 一键性能模式
GOVERNOR="schedutil"
for cpu_dir in /sys/devices/system/cpu/cpu[0-9]*/cpufreq; do
cpu=$(basename $(dirname $cpu_dir))
governor_file="$cpu_dir/scaling_governor"
# 设置 governor
[ -f "$governor_file" ] && echo "$GOVERNOR" > "$governor_file"
# 限制不低于最大频率的 80%
max_freq=$(cat "$cpu_dir/scaling_max_freq")
min_freq=$((max_freq * 80 / 100))
echo "$min_freq" > "$cpu_dir/scaling_min_freq"
echo "[$cpu] governor=$GOVERNOR min=$((min_freq/1000))MHz max=$((max_freq/1000))MHz"
done
# 设置 turbo boost
if [ -f /sys/devices/system/cpu/intel_pstate/no_turbo ]; then
echo 0 > /sys/devices/system/cpu/intel_pstate/no_turbo
echo "Turbo Boost: enabled"
fi
6.3 温度触发的调频限制
# 当温度超过 80°C 时自动限制最大频率到 2.2GHz
while true; do
temp=$(cat /sys/class/thermal/thermal_zone0/temp)
temp_c=$((temp / 1000))
if [ "$temp_c" -gt 80 ]; then
# 限频
for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do
echo 2200000 > "$f"
done
logger -t thermal "Temperature ${temp_c}C > 80C, limited to 2.2GHz"
elif [ "$temp_c" -lt 70 ]; then
# 恢复全频
for f in /sys/devices/system/cpu/cpu*/cpufreq/scaling_max_freq; do
cat "$(dirname $f)/cpuinfo_max_freq" > "$f"
done
logger -t thermal "Temperature ${temp_c}C < 70C, restored max freq"
fi
sleep 5
done
6.4 stress-ng 压力测试验证
# 安装 stress-ng
# 生成指定数量的 CPU 压力(每个 100% 利用率)
stress-ng --cpu 8 --cpu-method all --timeout 120s --metrics
# 同时在另一个终端观察调频行为
watch -n 1 'cat /proc/cpuinfo | grep MHz'
# 观察温度变化
watch -n 1 'cat /sys/class/thermal/thermal_zone0/temp'
# 结合 schedutil 验证升频速度
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# → schedutil
# 实时观察频率调整
turbostat --show Core,CPU,Avg_MHz,Busy%,Bzy_MHz,TSC_MHz,PkgTmp 1
6.5 msr-tools 查看实际核心频率
# MSR 0xCE (MSR_PLATFORM_INFO) 包含最大非 turbo 频率
rdmsr -a -f 15:8 0xCE
# MSR 0x1AD / 0x1AC / 0x1AB (TURBO_RATIO_LIMIT) 包含各核心数最大 turbo
rdmsr -a 0x1AD # 1-2 核心最大 turbo 倍频
rdmsr -a 0x1AC # 3-4 核心
rdmsr -a 0x1AB # 5-6 核心
# 使用 turbostat 频率监控(Turbo)
turbostat -i 1 -n 3
七、生产环境案例分析
7.1 案例一:高负载 Web 服务器 CPU 空闲异常
问题:Dell PowerEdge R740,Ubuntu 22.04,双 Xeon,在中等负载(30%)下 CPU 温度持续飙升至 95°C。
排查过程:
# 1. 确认 governor 配置
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor
# → performance ← 问题根源:强制高频运行
# 2. 查看散热策略
cat /sys/class/thermal/thermal_zone0/policy
# → step_wise
# 3. 查看实际频率分布
turbostat --quiet --show PkgTmp,Busy%,Bzy_MHz 1 5
# PkgTmp Busy% Bzy_MHz
# 94 32.5 3800 ← 全核 turbo 即使只有30%负载
# 95 31.8 3800
# 4. 确认散热是否正常
ipmitool sensor | grep -i fan
# FAN1_TACH → 12000 RPM (正常)
# FAN2_TACH → 11800 RPM (正常)
修复方案:
# 将 governor 切换为 schedutil
for gov in /sys/devices/system/cpu/cpu*/cpufreq/scaling_governor; do
echo schedutil > "$gov"
done
# 限制 turbo(可选,针对散热不佳数据中心)
echo 40 > /sys/devices/system/cpu/intel_pstate/max_perf_pct
# 将最大性能限制为标称频率的40%
效果:温度从 94°C 降至 68°C,PUE 改善 15%,性能损失 < 5%(因负载本身就低)。
7.2 案例二:ARM 大小核动态调频优化
问题:基于 RK3588 的嵌入式设备,小核在轻负载时被 schedutil 拉到高频,导致能耗浪费。
排查:
# 查看 CPU0(小核)OPP 表
cat /sys/devices/system/cpu/cpu0/cpufreq/stats/time_in_state
# freq(KHz) time(ms)
# 408000 102000 ← 大部分时间在低频,正确
# 600000 15000
# 但 CPU4(大核)被小任务拉起
cat /sys/devices/system/cpu/cpu4/cpufreq/stats/time_in_state
# 实际大核频繁被唤醒
修复:设置大小核差异化 governor 阈值
# 小核设置更保守的升频阈值(up_rate_limit_us)
echo 2000 > /sys/devices/system/cpu/cpu0/cpufreq/schedutil/up_rate_limit_us
echo 2000 > /sys/devices/system/cpu/cpu1/cpufreq/schedutil/up_rate_limit_us
# 大核保持快速响应
echo 500 > /sys/devices/system/cpu/cpu4/cpufreq/schedutil/up_rate_limit_us
7.3 案例三:CPU 热插拔死锁排查
问题:SMP 启动时 CPU hotplug 触发 kernel panic,调用栈涉及 stop_machine()。
根因分析:
// 典型场景:CPU_DYING 回调中试图获取一个在 stop_machine 上下文中已持有的锁
// 导致 AB-BA 死锁
// 解决方案:使用 cpuhp_setup_state_nocalls() 注册不在全核停止时执行的回调
// 或改用工作队列异步处理
八、关键内核参数速查表
8.1 热插拔相关
| Sysfs Path | 说明 | 典型值 |
|---|---|---|
/sys/devices/system/cpu/cpuN/online |
核心在线状态 | 0/1 |
/sys/devices/system/cpu/hplug/max_cpus |
最大可上线核心数 | 0xFF |
/proc/sys/kernel/cpu_offline_disabled |
禁止热插拔 | 0 |
8.2 cpufreq 相关
| Sysfs Path | 说明 | 典型值 |
|---|---|---|
scaling_governor |
当前 governor | schedutil |
scaling_min_freq |
最小频率 | 400000 (kHz) |
scaling_max_freq |
最大频率 | 3800000 (kHz) |
scaling_cur_freq |
当前频率 | 动态变化 |
schedutil/rate_limit_us |
调频速率限制 | 0-10000 |
energy_performance_preference |
能效偏好 | performance/balance_performance |
8.3 Intel P-State 特有
| Sysfs Path | 说明 | 典型值 |
|---|---|---|
intel_pstate/status |
p-state 驱动状态 | active/passive |
intel_pstate/no_turbo |
禁用 turbo | 0/1 |
intel_pstate/max_perf_pct |
最大性能百分比 | 100 |
intel_pstate/min_perf_pct |
最小性能百分比 | 4 |
intel_pstate/hwp_dynamic_boost |
动态 turbo 增强 | 0/1 |
8.4 Thermal 相关
| Sysfs Path | 说明 | 典型值 |
|---|---|---|
thermal_zone0/temp |
温度 (m°C) | 45000-95000 |
thermal_zone0/policy |
governor | fair_share/power_allocator |
thermal_zone0/passive |
被动冷却延迟 (ms) | 1000 |
cooling_device0/cur_state |
冷却级别 | 0 = 不冷却 |
cooling_device0/max_state |
最大冷却级别 | 变量 |
九、最佳实践与建议
9.1 调频 Governor 选择指南
| 场景 | 推荐 Governor | 理由 |
|---|---|---|
| 高频交易 / 实时系统 | performance | 零调频延迟 |
| 一般桌面 / 服务器 | schedutil | 快速响应 + 节能 |
| 电池供电 IoT | powersave 或自定义 ondemand | 最大化续航 |
| 虚拟机场景 | passive + host governor | 由 host 管理频率 |
| 嵌入式大核+小核 | schedutil + EAS | 能效感知调度 |
9.2 健康监控指标
# 安装必要工具
apt install linux-tools-common linux-tools-$(uname -r)
# 监控温度(每分钟)
while sleep 60; do
echo "$(date) temp=$(cat /sys/class/thermal/thermal_zone0/temp) \
freq=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq) \
governor=$(cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor)" \
>> /var/log/cpu_thermal.log
done &
# 使用 Prometheus Node Exporter 的 cpu collector 持续追踪
# 或通过 eBPF 程序实时采样
9.3 已知陷阱
- get_online_cpus() 内不能 sleep:该函数使当前 CPU 不可下线,但持有信号量时不能睡眠
- cpufreq 调频时的 IPI 风暴:
schedutil在极端负载波动时可能导致频繁写 MSR,使用rate_limit_us平滑 - 关机瞬间 hotplug race:模块卸载时若 cpu_online_map 正在变化,可能导致 use-after-free
- AMD 平台 STAPM:AMD 的 Skin Temperature Aware Power Management 会绕过 cpufreq 直接限频
- Hybrid 架构线程调度不对称:在混合架构(P-core + E-core)中,E-core 的 turbo 上限被硬编码限制
十、总结
CPU Hotplug 与 cpufreq 是理解现代系统功耗与性能的钥匙。它们不是孤立的子系统——cpufreq 的 governor 选型直接依赖 schedutil 从 scheduler 获取的负载数据,而 thermal governor 通过 cpufreq 作为冷却执行器;反过来,CPU hotplug 又可以在极端情况下直接下线核心作为最终散热手段。
掌握这组相互作用、内建 cpufreq 与 thermal 子系统的联调能力,是从系统管理员跃迁到系统架构师的关键一环。
记忆口诀:hotplug 管"开不开",governor 管"快不快",thermal 管"热不热"。三者联动,缺一不可。

发表评论 取消回复