嵌入式 Linux 系统的电源管理技巧
在嵌入式 Linux 系统中,电源管理至关重要,它不仅能延长设备的电池续航时间,还能降低设备的功耗和发热。本文将深入探讨嵌入式 Linux 系统中多种电源管理方法,包括 CPU 频率调节、空闲状态选择、外设电源管理以及系统睡眠模式等。
1. CPU 频率调节
利用 PowerTOP 工具,我们可以查看 CPU 在每个 OPP(Operating Performance Point)状态下所花费的时间百分比。在大多数情况下,ondemand 调速器是最佳选择。以下是选择特定调速器的两种方法:
- 内核配置 :通过配置内核,设置默认的调速器,例如 CPU_FREQ_DEFAULT_GOV_ONDEMAND 。
- 启动脚本 :使用启动脚本在系统启动时更改调速器。在 Debian 系统中,可参考 MELP/Chapter15/cpufrequtils 中的 System V 初始化脚本示例。
若想了解更多关于 CPUFreq 驱动的信息,可查看 Linux 内核源代码树中 Documentation/cpu-freq 目录下的文件。
2. 选择最佳空闲状态
当处理器没有任务时,会执行暂停指令并进入空闲状态,此时 CPU 功耗降低。当发生硬件中断等事件时,CPU 会退出空闲状态。大多数 CPU 有多个空闲状态,功耗和退出延迟各有不同,在 ACPI 规范中称为 C 状态。
选择合适空闲状态的关键在于预估 CPU 空闲的时长。可参考以下因素:
- 当前 CPU 负载 :若当前负载高,近期可能持续高负载,深度睡眠无益。
- 定时器事件 :即使负载低,也要查看是否有即将到期的定时器事件。若无负载和定时器,可选择更深的空闲状态。
Linux 中负责选择最佳空闲状态的是 CPUIdle 驱动,相关信息可在 Linux 内核源代码树的 Documentation/cpuidle 目录中找到。
2.1 CPUIdle 驱动
CPUIdle 由驱动和调速器组成,与 CPUFreq 不同,其调速器不能在运行时更改,也没有用户空间调速器接口。
CPUIdle 在 /sys/devices/system/cpu/cpu0/cpuidle 目录下展示每个空闲状态的信息,每个睡眠状态有一个子目录,从 state0 到 stateN , state0 为最轻睡眠, stateN 为最深睡眠。每个状态包含以下文件:
| 文件 | 描述 |
| ---- | ---- |
| desc | 状态的简短描述 |
| disable | 写入 1 可禁用此状态 |
| latency | CPU 核心退出此状态恢复正常运行所需的时间(微秒) |
| name | 状态名称 |
| power | 此空闲状态下的功耗(毫瓦) |
| time | 在此空闲状态下花费的总时间(微秒) |
| usage | 进入此状态的次数 |
以 BeagleBone Black 上的 AM335x SoC 为例,有两个空闲状态:
$ cd /sys/devices/system/cpu/cpu0/cpuidle
$ grep "" state0/*
state0/desc:ARM WFI
state0/disable:0
state0/latency:1
state0/name:WFI
state0/power:4294967295
state0/residency:1
state0/time:1023898
state0/usage:1426
此状态名为 WFI,指 ARM 暂停指令 Wait For Interrupt,延迟为 1 微秒,功耗显示为 -1 表示未知。
$ cd /sys/devices/system/cpu/cpu0/cpuidle
$ grep "" state1/*
state1/desc:mpu_gate
state1/disable:0
state1/latency:130
state1/name:mpu_gate
state1/power:0
state1/residency:300
state1/time:139156260
state1/usage:7560
此状态名为 mpu_gate,延迟为 130 微秒。空闲状态可硬编码在 CPUIdle 驱动中,也可在设备树中呈现,如 am33xx.dtsi 文件的部分内容:
cpus {
cpu@0 {
compatible = "arm,cortex-a8";
enable-method = "ti,am3352";
device_type = "cpu";
reg = <0>;
...
cpu-idle-states = <&mpu_gate>;
};
idle-states {
mpu_gate: mpu_gate {
compatible = "arm,idle-state";
entry-latency-us = <40>;
exit-latency-us = <90>;
min-residency-us = <300>;
ti,idle-wkup-m3;
};
};
}
CPUIdle 有两种调速器:
- ladder :根据上一次空闲时间,逐次降低或提高空闲状态。适用于固定定时器节拍的系统。
- menu :根据预期空闲时间选择空闲状态,适用于动态节拍系统。
用户可根据 NO_HZ 配置选择合适的调速器。在 /sys/devices/system/cpu/cpuidle 目录下,有两个文件显示当前使用的驱动和调速器:
- current_driver :显示 cpuidle 驱动名称。
- current_governor_ro :显示调速器名称。
空闲状态也可在 PowerTOP 的 Idle stats 选项卡中查看。
3. 无节拍操作
无节拍(NO_HZ)选项可进一步节省功耗。传统 Linux 系统使用定时器节拍作为测量超时的主要时间基准,但在无定时器事件时唤醒 CPU 处理定时器中断是浪费的。动态节拍内核配置选项 CONFIG_NO_HZ_IDLE 会在定时器处理例程结束时查看定时器队列,安排下一次中断时间,避免不必要的唤醒,使 CPU 长时间空闲。在对功耗敏感的应用中,应启用此选项。
4. 外设电源管理
在嵌入式 Linux 系统中,除 CPU 外,其他外设也可通过电源管理降低能耗。Linux 内核中的运行时电源管理系统(runtime pm)可与支持该功能的驱动配合,关闭未使用的设备,在需要时唤醒它们,对用户空间透明。
运行时电源管理通过 sysfs 接口实现,每个设备的 power 子目录下有以下文件:
| 文件 | 描述 |
| ---- | ---- |
| control | 用户空间可设置是否使用运行时电源管理, auto 启用, on 禁用 |
| runtime_enabled | 报告运行时电源管理的启用、禁用状态,若 control 为 on ,则报告 forbidden |
| runtime_status | 报告设备当前状态,如 active 、 suspended 或 unsupported |
| autosuspend_delay_ms | 设备进入暂停状态前的延迟时间,-1 表示永远等待。部分驱动为避免频繁暂停/恢复循环而实现此功能 |
以 BeagleBone Black 上的 MMC 驱动为例:
$ cd /sys/devices/platform/ocp/481d8000.mmc/mmc_host/mmc1/mmc1:0001/power
$ grep "" *
async:enabled
autosuspend_delay_ms:3000
control:auto
runtime_active_kids:0
runtime_active_time:14464
runtime_enabled:enabled
runtime_status:suspended
runtime_suspended_time:121208
runtime_usage:0
此时运行时电源管理已启用,设备处于暂停状态,上次使用后 3000 毫秒会再次暂停。读取设备块后:
$ sudo dd if=/dev/mmcblk1p3 of=/dev/null count=1
1+0 records in
1+0 records out
512 bytes copied, 0.00629126 s, 81.4 kB/s
$ grep "" *
async:enabled
autosuspend_delay_ms:3000
control:auto
runtime_active_kids:0
runtime_active_time:17120
runtime_enabled:enabled
runtime_status:active
runtime_suspended_time:178520
runtime_usage:0
MMC 驱动变为活动状态,板卡功耗从 320 mW 增至 500 mW。3 秒后再次暂停,功耗恢复到 320 mW。
若想了解更多运行时电源管理信息,可查看 Linux 内核源代码中的 Documentation/power/runtime_pm.txt 文件。
5. 系统睡眠模式
将整个系统置于睡眠模式也是一种有效的电源管理技术。在 Linux 内核中,系统睡眠通常由用户发起,常见的有两种模式:
- Suspend(挂起到内存) :除系统内存外,其他设备关闭,机器仍消耗少量电力。唤醒时,内存保留之前状态,设备可快速恢复运行。
- Hibernate(挂起到磁盘) :将内存内容保存到硬盘,系统完全无功耗,可长时间保持此状态。但唤醒时需从磁盘恢复内存,速度较慢,在嵌入式系统中很少使用。
Linux 支持四种睡眠状态,对应 ACPI 规范中的 S 状态:
| Linux 睡眠状态 | ACPI S 状态 | 描述 |
| ---- | ---- | ---- |
| freeze ([S0]) | [S0] | 停止用户空间所有活动,但 CPU 和内存正常运行,因无用户空间代码运行而节省功耗 |
| standby (S1) | S1 | 类似 freeze,但除引导 CPU 外,其他 CPU 离线 |
| mem (S3) | S3 | 关闭系统,将内存置于自刷新模式,即挂起到内存 |
| disk (S4) | S4 | 将内存内容保存到硬盘并关闭系统,即挂起到磁盘 |
要查看系统支持的睡眠状态,可执行以下命令:
# cat /sys/power/state
freeze standby mem disk
要进入系统睡眠状态,只需将所需状态写入 /sys/power/state 文件。例如,将 BeagleBone Black 挂起到内存:
# echo mem > /sys/power/state
[ 1646.158274] PM: Syncing filesystems ...done.
[ 1646.178387] Freezing user space processes ... (elapsed 0.001 seconds) done.
[ 1646.188098] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
[ 1646.197017] Suspending console(s) (use no_console_suspend to debug)
[ 1646.338657] PM: suspend of devices complete after 134.322 msecs
[ 1646.343428] PM: late suspend of devices complete after 4.716 msecs
[ 1646.348234] PM: noirq suspend of devices complete after 4.755 msecs
[ 1646.348251] Disabling non-boot CPUs ...
[ 1646.348264] PM: Successfully put all powerdomains to target state
设备在不到一秒内关机,功耗降至 10 毫瓦以下。
6. 唤醒事件
在设备进入睡眠状态前,必须有唤醒方法。若没有至少一个唤醒源,系统将拒绝进入睡眠状态,并显示错误信息: No sources enabled to wake-up! Sleep abort. 。
唤醒事件通过 sysfs 控制,每个设备的 power 子目录下的 wakeup 文件包含以下信息:
- enabled :设备可产生唤醒事件。
- disabled :设备不会产生唤醒事件。
- (empty) :设备不具备产生唤醒事件的能力。
要获取可产生唤醒事件的设备列表,可执行以下命令:
$ find /sys/devices/ -name wakeup | xargs grep "abled"
以 BeagleBone Black 为例,UART 是唤醒源,按下控制台按键可唤醒设备:
[ 1646.348264] PM: Wakeup source UART
[ 1646.368482] PM: noirq resume of devices complete after 19.963 msecs
[ 1646.372482] PM: early resume of devices complete after 3.192 msecs
[ 1646.795109] net eth0: initializing cpsw version 1.12 (0)
[ 1646.798229] net eth0: phy found : id is : 0x7c0f1
[ 1646.798447] libphy: PHY 4a101000.mdio:01 not found
[ 1646.798469] net eth0: phy 4a101000.mdio:01 not found on slave 1
[ 1646.927874] PM: resume of devices complete after 555.337 msecs
[ 1647.003829] Restarting tasks ... done.
7. 实时时钟定时唤醒
大多数系统的实时时钟(RTC)可在未来 24 小时内产生闹钟中断。若 /sys/class/rtc/rtc0 目录存在,其中的 wakealarm 文件可用于设置唤醒时间。写入一个数字,RTC 将在该数字对应的秒数后产生闹钟。若同时启用 RTC 的唤醒事件,RTC 可唤醒暂停的设备。
例如,使用 rtcwake 命令将系统置于待机状态,5 秒后由 RTC 唤醒:
$ sudo su –
# rtcwake -d /dev/rtc0 -m standby -s 5
rtcwake: assuming RTC uses UTC ...
rtcwake: wakeup from "standby" using /dev/rtc0 at Tue Dec 1 19:34:10 2020
[ 187.345129] PM: suspend entry (shallow)
[ 187.345148] PM: Syncing filesystems ... done.
[ 187.346754] Freezing user space processes ... (elapsed 0.003 seconds) done.
[ 187.350688] OOM killer disabled.
[ 187.350789] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done.
[ 187.352361] Suspending console(s) (use no_console_suspend to debug)
[ 187.500906] Disabling non-boot CPUs ...
[ 187.500941] pm33xx pm33xx: PM: Successfully put all powerdomains to target state
[ 187.500941] PM: Wakeup source RTC Alarm
[ 187.529729] net eth0: initializing cpsw version 1.12 (0)
[ 187.605061] SMSC LAN8710/LAN8720 4a101000.mdio:00: attached PHY driver [SMSC LAN8710/LAN8720] (mii_bus:phy_addr=4a101000.mdio:00, irq=POLL)
[ 187.731543] OOM killer enabled.
[ 187.731563] Restarting tasks ... done.
[ 187.756896] PM: suspend exit
由于 UART 也是唤醒源,在 RTC 闹钟到期前按下控制台按键也可唤醒 BeagleBone Black。此外,BeagleBone Black 上的电源按钮也是唤醒源,在没有串口控制台时可用于从待机状态恢复。但要注意按下电源按钮而非复位按钮,否则设备将重启。
综上所述,通过合理运用 CPU 频率调节、空闲状态选择、外设电源管理、系统睡眠模式以及唤醒机制等电源管理技术,可有效降低嵌入式 Linux 系统的功耗,延长设备电池续航时间。
嵌入式 Linux 系统的电源管理技巧(下半部分)
8. 电源管理技术总结与应用建议
为了更清晰地展示各种电源管理技术的特点和适用场景,我们可以用一个表格来总结:
| 电源管理技术 | 特点 | 适用场景 |
| ---- | ---- | ---- |
| CPU 频率调节(ondemand 调速器) | 根据 CPU 负载动态调整频率 | 负载变化频繁的场景 |
| 选择最佳空闲状态(CPUIdle 驱动) | 根据 CPU 空闲时长选择合适状态 | 有明显空闲时间的场景 |
| 无节拍操作(NO_HZ) | 避免不必要的定时器唤醒 | 对功耗敏感,空闲时间较长的场景 |
| 外设电源管理(runtime pm) | 动态管理外设电源 | 有多个外设且部分外设使用不频繁的场景 |
| 系统睡眠模式(Suspend、Hibernate) | 整体降低系统功耗 | 设备长时间不使用的场景 |
| 唤醒事件(UART、RTC、Power 按钮等) | 确保系统能从睡眠状态唤醒 | 所有使用系统睡眠模式的场景 |
下面我们通过一个 mermaid 流程图来展示一个完整的电源管理流程:
graph TD;
A[系统启动] --> B{CPU 负载高?};
B -- 是 --> C[使用 ondemand 调速器调节频率];
B -- 否 --> D{有定时器事件?};
D -- 是 --> E[正常运行,等待事件处理];
D -- 否 --> F[选择最佳空闲状态];
F --> G{设备空闲时间长?};
G -- 是 --> H[启用 NO_HZ 无节拍操作];
G -- 否 --> I[保持当前状态];
I --> J{是否有外设不使用?};
J -- 是 --> K[使用 runtime pm 关闭外设];
J -- 否 --> L[继续运行];
K --> M{用户发起睡眠请求?};
M -- 是 --> N[选择睡眠模式(Suspend 或 Hibernate)];
M -- 否 --> L;
N --> O{设置唤醒事件(UART、RTC、Power 按钮等)};
O --> P[系统进入睡眠状态];
P --> Q{发生唤醒事件?};
Q -- 是 --> R[系统唤醒,恢复运行];
Q -- 否 --> P;
9. 实际案例分析
以 BeagleBone Black 为例,我们来分析如何综合运用上述电源管理技术。
9.1 CPU 频率调节
在 BeagleBone Black 上,我们可以通过以下步骤选择 ondemand 调速器:
1. 配置内核,设置 CPU_FREQ_DEFAULT_GOV_ONDEMAND 。
2. 或者使用启动脚本,参考 Debian 系统中的 MELP/Chapter15/cpufrequtils 中的 System V 初始化脚本示例。
9.2 选择最佳空闲状态
通过查看 /sys/devices/system/cpu/cpu0/cpuidle 目录下的信息,我们了解到 BeagleBone Black 有两个空闲状态(WFI 和 mpu_gate)。根据 CPU 负载和空闲时间,我们可以选择合适的状态。例如,当 CPU 负载较低且空闲时间较短时,可以选择 WFI 状态;当空闲时间较长时,可以选择 mpu_gate 状态。
9.3 无节拍操作
在对功耗敏感的应用中,我们可以在编译内核时启用 CONFIG_NO_HZ_IDLE 选项,让 CPU 能够长时间处于空闲状态,避免不必要的唤醒。
9.4 外设电源管理
对于 BeagleBone Black 上的 MMC 驱动,我们可以通过以下操作实现电源管理:
1. 进入设备的 power 子目录:
$ cd /sys/devices/platform/ocp/481d8000.mmc/mmc_host/mmc1/mmc1:0001/power
- 设置
control文件为auto以启用运行时电源管理:
$ echo auto > control
- 查看设备状态:
$ grep "" *
9.5 系统睡眠模式
要将 BeagleBone Black 挂起到内存,我们可以执行以下命令:
# echo mem > /sys/power/state
在进入睡眠状态前,需要确保设置了合适的唤醒事件,例如 UART、RTC 或 Power 按钮。
9.6 唤醒事件设置
- UART 唤醒 :确保 UART 设备的
wakeup文件设置为enabled。
$ echo enabled > /sys/devices/.../power/wakeup
- RTC 唤醒 :使用
rtcwake命令设置唤醒时间。
$ sudo su –
# rtcwake -d /dev/rtc0 -m standby -s 5
- Power 按钮唤醒 :确保按下的是 Power 按钮而不是 Reset 按钮。
10. 注意事项和常见问题解决
在进行电源管理的过程中,可能会遇到一些问题,下面我们来总结一些注意事项和常见问题的解决方法:
- 唤醒失败 :
- 检查是否至少有一个唤醒源被启用。可以通过以下命令查看:
$ find /sys/devices/ -name wakeup | xargs grep "abled"
- 确保唤醒源设备正常工作,例如 UART 设备是否有数据传输,RTC 是否正常计时。
- 系统睡眠模式不稳定 :
- 检查内核配置是否正确,特别是与系统睡眠模式相关的选项。
- 查看系统日志,查找是否有错误信息,例如
dmesg命令。
- 外设电源管理异常 :
- 检查设备驱动是否支持 runtime pm。
- 确保
power子目录下的文件设置正确,例如control文件是否为auto。
通过以上的总结和分析,我们可以看到,嵌入式 Linux 系统的电源管理是一个复杂而又重要的话题。通过合理运用各种电源管理技术,我们可以有效地降低系统功耗,延长设备的电池续航时间,提高设备的性能和稳定性。在实际应用中,我们需要根据具体的场景和需求,选择合适的电源管理技术,并注意相关的注意事项和常见问题的解决方法。
超级会员免费看
915

被折叠的 条评论
为什么被折叠?



