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);
}

关键工作流程:

  1. CFS runqueue 通过 cpufreq_update_util() 在每次任务入队/出队时调用回调
  2. 回调记录当前 CPU 利用率(sched_cpu_util())
  3. 调度器 tick 或 idle 入口时触发频率计算
  4. 计算使用 freq = max_freq * (util / max_cap) 公式
  5. 通过 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 已知陷阱

  1. get_online_cpus() 内不能 sleep:该函数使当前 CPU 不可下线,但持有信号量时不能睡眠
  2. cpufreq 调频时的 IPI 风暴:schedutil 在极端负载波动时可能导致频繁写 MSR,使用 rate_limit_us 平滑
  3. 关机瞬间 hotplug race:模块卸载时若 cpu_online_map 正在变化,可能导致 use-after-free
  4. AMD 平台 STAPM:AMD 的 Skin Temperature Aware Power Management 会绕过 cpufreq 直接限频
  5. 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 管"热不热"。三者联动,缺一不可。

点赞(0) 打赏

评论列表 共有 0 条评论

暂无评论
立即
投稿

微信公众账号

微信扫一扫加关注

发表
评论
返回
顶部