云计算百科
云计算领域专业知识百科平台

【Linux内核专栏 15】电源管理

15 · 电源管理

本篇定位:嵌入式和移动设备,省电是硬指标。FreeRTOS 用 tickless idle + WFI;Linux 有完整电源管理框架:cpuidle(空闲状态)、cpufreq(频率调节)、Runtime PM(运行时按需开关设备)、系统级 suspend/hibernate、wakeup source(唤醒源)。本篇讲清这套,读完能给设备配省电策略、能调"为什么设备不休眠"。

Linux 电源管理是tickless/WFI 的系统化扩展 FreeRTOS 省电:tickless idle(进 WFI,停 tick)。Linux 省电复杂得多:① cpuidle(多级空闲状态,浅睡/深睡);② cpufreq(动态调频,按负载);③ Runtime PM(设备不用就关);④ suspend(整机睡眠);⑤ wakeup source(什么能唤醒)。增量是"多级 + 设备级 + 系统级"。WFI 是 cpuidle 最浅一级。


目录

  • 15 · 电源管理
  • 一、电源管理的层次
  • 二、cpuidle:CPU 空闲状态
    • 2.1 为什么多级空闲
    • 2.2 idle state
    • 2.3 RISC-V idle
    • 2.4 看 cpuidle
    • 2.5 对照 FreeRTOS tickless
  • 三、cpufreq:动态调频(DVFS)
    • 3.1 DVFS 是什么
    • 3.2 cpufreq 框架
    • 3.3 governor(策略)
    • 3.4 看与配
    • 3.5 OPP(Operating Performance Points)
  • 四、Runtime PM:设备运行时电源管理⭐
    • 4.1 为什么 Runtime PM
    • 4.2 Runtime PM API
    • 4.3 runtime PM callback
    • 4.4 引用计数
    • 4.5 autosuspend(延迟挂起)
    • 4.6 看 Runtime PM
  • 五、系统级 Suspend(睡眠)
    • 5.1 suspend 状态
    • 5.2 suspend 流程
    • 5.3 沿设备树级联(10 篇)
    • 5.4 PM QoS(电源管理服务质量)
    • 5.5 触发 suspend
  • 六、wakeup source(唤醒源)
    • 6.1 什么是唤醒源
    • 6.2 注册唤醒源
    • 6.3 看唤醒源
    • 6.4 唤醒原因排查
  • 七、clock 和 regulator 框架
    • 7.1 clock framework
    • 7.2 regulator framework
    • 7.3 与 PM 关系
  • 八、PM ops 完整结构
  • 九、嵌入式省电策略
      • 9.1 常见策略
    • 9.2 嵌入式典型场景
    • 9.3 调试省电
  • 十、对照总表
  • 十一、本篇小结
  • 速查表

一、电源管理的层次

系统级: suspend(sleep) / hibernate(磁盘) / shutdown
│
子系统级: Runtime PM(设备按需开关)
│
CPU 级: cpufreq(调频) + cpuidle(空闲状态)
│
硬件: DVFS(电压频率) / WFI / clock gating / power gating

层次作用触发
系统级 整机睡眠/休眠 用户(电源键)/超时
子系统级 设备按需开关 设备不忙时自动
CPU 级 调频/空闲 负载/空闲

二、cpuidle:CPU 空闲状态

2.1 为什么多级空闲

  • CPU 空闲可进不同睡眠深度
  • 浅睡(WFI):唤醒快,省电少
  • 深睡(power gating):唤醒慢,省电多
  • cpuidle 框架:按预计空闲时间选最优状态

2.2 idle state

struct cpuidle_state {
char name[CPUIDLE_NAME_LEN];
unsigned int flags;
int (*enter)(struct cpuidle_device *dev,
struct cpuidle_driver *drv, int index);
int exit_latency; // 退出延迟(μs,唤醒要多久)
int target_residency; // 目标驻留(μs,至少睡这么久才划算)
int power_usage; // 功耗(mW)
...
};

  • 每个 state 有退出延迟 + 目标驻留
  • idle 时间短选浅睡(快醒),长选深睡(省电)

2.3 RISC-V idle

  • 最浅:WFI(Wait For Interrupt),指令进低功耗
  • 更深:SoC 特定(关 PLL/关域)
  • cpuidle driver 注册多级

2.4 看 cpuidle

cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name
# WFI
# RETENTION
# POWER_OFF

cat /sys/devices/system/cpu/cpu0/cpuidle/state*/disable
# 0 (0=启用,1=禁用某状态)

2.5 对照 FreeRTOS tickless

FreeRTOS ticklessLinux cpuidle
空闲进 WFI(常一级) 多级 idle state
选哪级 单级 按预计空闲时间
唤醒 中断 中断
tick 停 停(NOHZ,08 篇)

tickless WFI 是 cpuidle 最浅级 FreeRTOS tickless 进 WFI——Linux cpuidle 的最浅 state(WFI)。Linux 多了深睡状态(关域/关 PLL),按空闲时长选。增量是多级 + 智能选择。WFI 经验直接迁移,新增"深睡的延迟/驻留权衡"。


三、cpufreq:动态调频(DVFS)

3.1 DVFS 是什么

  • Dynamic Voltage and Frequency Scaling
  • 负载低降频降压(省电),负载高升频升压(性能)
  • CPU + 调频驱动 + governor(策略)

3.2 cpufreq 框架

struct cpufreq_driver {
int (*init)(struct cpufreq_policy *policy);
int (*setpolicy)(...); // 设策略
int (*target_index)(...); // 设频率
...
};

struct cpufreq_governor {
int (*throttle)(...);
unsigned int (*get)(...);
...
};

  • driver:硬件操作(SoC 调频接口)
  • governor:策略(什么时候调多少)

3.3 governor(策略)

governor策略用途
performance 一直最高频 性能优先
powersave 一直最低频 省电优先
userspace 用户态指定 手动
ondemand 按负载升频 通用(默认)
schedutil 据调度器信息调 现代(配合 EAS)
conservative 渐进调频 平衡

3.4 看与配

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors
# ondemand performance powersave …

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq
# 1200000 (1.2 GHz)

cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_frequencies
# 400000 800000 1200000 1600000

# 改 governor
echo performance > /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor

# 设固定频率
echo 800000 > /sys/devices/system/cpu/cpu0/cpufreq/scaling_setspeed

3.5 OPP(Operating Performance Points)

  • SoC 定义的"频率+电压"组合
  • DTS 描述:operating-points-v2
  • 调频驱动据 OPP 切换

cpu0_opp_table: opp-table {
compatible = "oper-points-v2";
opp-400M { opp-hz = <400000000>; opp-microvolt = <900000>; };
opp-1G { opp-hz = <1000000000>; opp-microvolt = <1100000>; };
};


四、Runtime PM:设备运行时电源管理⭐

4.1 为什么 Runtime PM

  • 设备不忙时关它(省电),要用时开
  • 不等整机 suspend,运行时动态开关
  • 例:USB 设备空闲自动挂起

4.2 Runtime PM API

// 设备结构(继承 device,10 篇)
struct device {
...
struct dev_pm_info power; // PM 状态
...
};

// 驱动用:
pm_runtime_get_sync(dev); // 标记"要用",+1 计数,若 0→1 调 resume
pm_runtime_put_sync(dev); // 标记"用完",-1,若 1→0 调 suspend

// 或自动:
pm_runtime_enable(dev);
pm_runtime_set_autosuspend_delay(dev, 2000); // 空闲 2s 后自动挂起
pm_runtime_use_autosuspend(dev);
pm_runtime_mark_last_busy(dev);

4.3 runtime PM callback

struct dev_pm_ops {
int (*runtime_suspend)(struct device *dev); // 挂起
int (*runtime_resume)(struct device *dev); // 恢复
int (*runtime_idle)(struct device *dev); // 空闲(可决定挂起)
...
};

// 驱动注册
static const struct dev_pm_ops my_pm = {
SET_RUNTIME_PM_OPS(my_runtime_suspend, my_runtime_resume, NULL)
};

4.4 引用计数

  • pm_runtime_get:计数 +1(要用)
  • pm_runtime_put:计数 -1(用完)
  • 计数 > 0:设备活跃
  • 计数 = 0:可挂起
  • 计数 0→1:调 resume;1→0:调 suspend

4.5 autosuspend(延迟挂起)

  • 不是用完立刻挂起(频繁开关损耗)
  • 空闲 N 秒后才挂起
  • pm_runtime_mark_last_busy + autosuspend_delay

Runtime PM 是设备级省电,你裸机手动开关外设 裸机省电:不用 UART 时手动关时钟。Linux Runtime PM 框架化:驱动实现 suspend/resume 回调,框架管引用计数,自动开关。写驱动加 dev_pm_ops,框架管时机。

4.6 看 Runtime PM

cat /sys/devices/.../power/control
# on (on=禁止自动挂起,auto=允许)

cat /sys/devices/.../power/runtime_status
# active / suspended / suspending / resuming

echo auto > /sys/devices/.../power/control # 允许自动
echo on > /sys/devices/.../power/control # 禁止(常开)


五、系统级 Suspend(睡眠)

5.1 suspend 状态

状态名字特点
freeze 冻结用户态 最浅,只停进程
standby 待机 系统进低功耗,内存保持
mem 挂起到内存(S3) 内存自刷新,CPU/多数外设关
disk 挂起到磁盘(休眠,S4) 内存存盘后整机关

5.2 suspend 流程

echo mem > /sys/power/state
→ 内核 suspend 流程:
1. 冻结用户进程(信号停止)
2. 通知所有设备 suspend(dev_pm_ops.suspend)
3. 关中断
4. 关 CPU(进睡眠)
… 等唤醒 …
唤醒:
5. 唤醒源触发
6. 恢复 CPU
7. 开中断
8. resume 设备(逆序)
9. 解冻进程

5.3 沿设备树级联(10 篇)

  • suspend 按设备树深度优先逆序:先子后父
  • resume 按设备树顺序:先父后子
  • 父设备(总线)先 resume,子设备才能用

5.4 PM QoS(电源管理服务质量)

  • 约束"不能睡太深"或"必须快醒"
  • 例:音频要低延迟,不能进深睡
  • pm_qos_add_request 加约束

5.5 触发 suspend

echo mem > /sys/power/state # 挂起到内存
echo disk > /sys/power/state # 休眠
echo freeze > /sys/power/state # 仅冻结

# 看支持的状态
cat /sys/power/state
# freeze mem disk


六、wakeup source(唤醒源)

6.1 什么是唤醒源

  • 系统睡眠时,只有特定源能唤醒(中断)
  • wakeup source 标记"我能唤醒系统"
  • 例:电源键、网络(Wake-on-LAN)、RTC 闹钟、GPIO

6.2 注册唤醒源

// 设备启用唤醒能力
device_set_wakeup_capable(dev, true);
device_wakeup_enable(dev);

// 中断唤醒(IRQF 之外)
enable_irq_wake(irq); // 让该中断能唤醒
disable_irq_wake(irq);

6.3 看唤醒源

cat /proc/acpi/wakeup # x86
cat /sys/kernel/debug/wakeup_sources # 通用

# 看哪些设备能唤醒
for d in /sys/devices/*/*/power/wakeup; do
echo "$d: $(cat $d)"
done

6.4 唤醒原因排查

# 看上次唤醒原因
cat /sys/kernel/debug/wakeup_sources
# active_count / event_count / wakeup_count / expire_count
# last_time / max_time / total_time

嵌入式唤醒调试是难点 嵌入式产品:“睡眠了但被意外唤醒"或"睡眠后唤醒不来”。排查:① /sys/kernel/debug/wakeup_sources 看哪个源触发;② 检查设备 wakeup_enabled;③ enable_irq_wake 是否对。这是嵌入式 BSP 省电调试的核心。


七、clock 和 regulator 框架

7.1 clock framework

  • SoC 时钟树复杂(PLL/分频/gating)
  • clock framework 统一管理
  • 驱动 clk_get/clk_prepare_enable/clk_disable_unprepare

struct clk *clk = clk_get(dev, NULL);
clk_prepare_enable(clk); // 开时钟
// 用设备
clk_disable_unprepare(clk);

7.2 regulator framework

  • 电源调节器(LDO/DCDC)
  • 驱动 regulator_get/regulator_enable/regulator_set_voltage

struct regulator *r = devm_regulator_get(dev, "vdd");
regulator_enable(r);
regulator_set_voltage(r, 1100000, 1100000);

7.3 与 PM 关系

  • cpufreq 调频要同步调压(调 regulator)
  • suspend 关时钟(clock gating)
  • DVFS = clock(频率)+ regulator(电压)联动

八、PM ops 完整结构

struct dev_pm_ops {
/* 系统 suspend/resume */
int (*suspend)(...); int (*resume)(...);
int (*freeze)(...); int (*thaw)(...);
int (*poweroff)(...); int (*restore)(...);
/* Runtime PM */
int (*runtime_suspend)(...);
int (*runtime_resume)(...);
int (*runtime_idle)(...);
/* … 完整版更多 */
};

// 驱动用宏简化
static const struct dev_pm_ops my_pm = {
SET_SYSTEM_SLEEP_PM_OPS(my_suspend, my_resume) // 系统 suspend
SET_RUNTIME_PM_OPS(my_rt_suspend, my_rt_resume, NULL) // Runtime PM
};

  • 系统 suspend 和 Runtime PM 是两套
  • 驱动可只实现一套或都实现

九、嵌入式省电策略

9.1 常见策略

策略工具
CPU 调频 cpufreq ondemand/schedutil
CPU 空闲 cpuidle 多级
设备按需开关 Runtime PM
整机睡眠 suspend mem(无任务时)
关外设时钟 clock gating
关未用域 power gating(SoC 级)
唤醒源 只留必要(电源键/RTC/网络)

9.2 嵌入式典型场景

  • 电池产品(手持):cpufreq ondemand + cpuidle 深睡 + Runtime PM + 空闲 suspend
  • 常电产品( plugged):cpufreq performance + 禁深睡(低延迟优先)
  • 边缘 AI:高性能时全频,空闲降频

9.3 调试省电

# 看功耗(若有功率计)
# 看 CPU 频率/状态
cat /sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq
cat /sys/devices/system/cpu/cpu*/cpuidle/state*/usage

# 看设备 PM 状态
cat /sys/devices/.../power/runtime_status

# 看唤醒源
cat /sys/kernel/debug/wakeup_sources

# powertop(省电分析工具)
powertop

嵌入式视角:powertop 是省电调优利器 Ubuntu 有 powertop,扫系统给出"哪些设备可省电"“哪些 wakeup 频繁”。嵌入式虽不一定有 powertop,但思路通用:① 找异常活跃设备;② 禁不必要唤醒;③ 配 Runtime PM。你 AEGIX 省电产品可参考。


十、对照总表

概念FreeRTOS/裸机Linux PM
CPU 空闲 WFI cpuidle 多级
调频 手动 cpufreq + governor
设备开关 手动 Runtime PM
整机睡眠 无(或自己写) suspend(mem/disk)
唤醒源 中断 wakeup source
时钟管理 手动配 clock framework
电压管理 手动 regulator framework
省电策略 tickless 多层组合
调试 电流计 powertop/wakeup_sources

十一、本篇小结

  • cpuidle:CPU 空闲多级状态(WFI 浅/深睡),按预计空闲时间选,exit_latency/target_residency 权衡
  • cpufreq:DVFS 动态调频,governor(performance/ondemand/schedutil),DTS OPP 描述频率电压
  • Runtime PM:设备运行时按需开关,引用计数(pm_runtime_get/put),autosuspend 延迟挂起,dev_pm_ops runtime_suspend/resume
  • 系统 suspend:freeze/standby/mem/disk,沿设备树级联(子先 suspend,父先 resume)
  • wakeup source:标记能唤醒系统的源,enable_irq_wake,排查 /sys/kernel/debug/wakeup_sources
  • clock framework:统一时钟管理(clk_prepare_enable);regulator framework:电源调压
  • PM ops:系统 suspend 和 Runtime PM 两套,SET_SYSTEM_SLEEP_PM_OPS / SET_RUNTIME_PM_OPS
  • 嵌入式省电:cpufreq ondemand + cpuidle 深睡 + Runtime PM + 空闲 suspend + 只留必要唤醒源
  • FreeRTOS tickless WFI = cpuidle 最浅级,增量是多级 + 设备级 + 系统级

速查表

想干啥用什么
看 CPU 频率 cat /sys/…/cpu/cpu0/cpufreq/scaling_cur_freq
改 governor echo ondemand > /sys/…/scaling_governor
设固定频率 echo Hz > /sys/…/scaling_setspeed
看 idle 状态 cat /sys/…/cpu0/cpuidle/state*/name
禁某 idle 状态 echo 1 > /sys/…/cpu0/cpuidle/state3/disable
看设备 PM 状态 cat /sys/devices/…/power/runtime_status
禁设备自动挂起 echo on > /sys/devices/…/power/control
系统挂起 echo mem > /sys/power/state
看支持睡眠状态 cat /sys/power/state
看唤醒源 cat /sys/kernel/debug/wakeup_sources
让中断唤醒 enable_irq_wake(irq)
开时钟 clk_prepare_enable(clk)
设电压 regulator_set_voltage
省电分析 powertop
Runtime PM 引用 pm_runtime_get_sync / pm_runtime_put_sync

💡技术之路漫漫,分享是为了更好地交流。如果本文的内容对你有启发,希望能得到你的 点赞 👍 和 收藏 ⭐。

如果你在调试过程中遇到了其他问题,欢迎在 评论区 💬 留言,我们一起探讨。也欢迎 关注 👀 我,一起交流底层开发的那些事儿。


赞(0)
未经允许不得转载:网硕互联帮助中心 » 【Linux内核专栏 15】电源管理
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!