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
| 空闲进 | 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(策略)
| 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 省电产品可参考。
十、对照总表
| 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 |
💡技术之路漫漫,分享是为了更好地交流。如果本文的内容对你有启发,希望能得到你的 点赞 👍 和 收藏 ⭐。
如果你在调试过程中遇到了其他问题,欢迎在 评论区 💬 留言,我们一起探讨。也欢迎 关注 👀 我,一起交流底层开发的那些事儿。
网硕互联帮助中心


评论前必须登录!
注册