4、Linux电源管理子系统概述

电源管理,说白了就是让系统该睡的时候睡,该醒的时候醒,别在那空转耗电。我做了这么多年嵌入式,见过太多产品因为电源管理没做好,电池续航直接砍半的案例。Linux的电源管理子系统,就是帮我们解决这个问题的核心框架。

这一章,我们把它拆开来看。从顶层架构到底层接口,从设备树配置到调试手段,一条线捋清楚。

4.1 Linux电源管理架构

Linux的电源管理,不是一个大泥球。它分了三层,各管各的事。我习惯把它想象成一个「三层指挥系统」:

  • PM Core:总指挥部。负责全局策略、状态切换、通知机制。
  • Runtime PM:基层班长。管设备用的时候供电,不用的时候断电,动态调整。
  • Suspend/Resume:大部队行动。整个系统一起睡,一起醒。

这三层配合得好,系统才能既省电又稳定。我在一个工业平板项目里,就见过Runtime PM和Suspend/Resume打架的情况——设备明明在跑,却被强行挂起了。嗯,这就是分层没协调好。

核心关系图:PM Core 向下调度 Runtime PM 和 Suspend/Resume,向上对接应用层的电源策略。

Linux电源管理架构 PM Core Runtime PM Suspend/Resume 设备驱动层(I2C、SPI、MMC、USB...) 每个驱动实现自己的 suspend/resume/runtime 回调 说明 PM Core 是大脑,Runtime PM 是日常节能,Suspend/Resume 是深度休眠

4.2 PM Core:总指挥部

PM Core 是电源管理的核心模块。它不直接操作硬件,而是提供一套框架,让驱动和子系统能注册进来。你想想看,如果没有这个统一调度,每个驱动自己搞一套电源管理,那系统不乱套才怪。

PM Core 主要干这几件事:

  • 管理电源状态机:系统级的状态切换(Active、Suspend、Hibernate)。
  • 通知链(Notification Chain):状态变化前、变化后,通知所有关心这件事的模块。
  • 回调注册:驱动通过 struct dev_pm_ops 注册自己的电源管理回调。

我记得第一次看 struct dev_pm_ops 的时候,被里面十几个回调函数吓到了。其实常用的就几个:.suspend、.resume、.runtime_suspend、.runtime_resume。其他的,用到再查就行。

小技巧:调试 PM Core 的问题,可以打开 CONFIG_PM_DEBUG 和 CONFIG_PM_SLEEP_DEBUG,内核会打印详细的电源状态切换日志。

4.3 Runtime PM:动态节能的利器

Runtime PM 是我个人最喜欢的机制。它让设备在空闲时自动断电,有请求时自动上电。整个过程对应用层透明——应用根本不知道设备被关过。

它的工作流程很简单:

  1. 设备空闲,驱动调用 pm_runtime_put_sync() 或 pm_runtime_put_autosuspend()。
  2. PM Core 检查引用计数,如果为0,调用驱动的 runtime_suspend 回调。
  3. 设备断电,进入低功耗状态。
  4. 有新的I/O请求,驱动调用 pm_runtime_get_sync()。
  5. PM Core 调用 runtime_resume 回调,设备上电。

这里有个坑,我踩过好几次:自动挂起超时(autosuspend delay)。如果设备频繁地在挂起和唤醒之间切换,反而更耗电。我建议给每个设备设置一个合理的超时时间,比如100ms。这样设备不会因为一次短暂的闲置就被关掉。

注意:Runtime PM 不是万能的。有些设备不支持动态电源管理,比如老旧的UART控制器。强行开启 Runtime PM 可能导致设备工作异常。

4.4 Suspend/Resume:系统级的睡眠与唤醒

Suspend/Resume 是系统级的操作。用户按下电源键,系统进入休眠;再按一下,系统恢复。这个过程涉及所有设备、CPU、内存控制器。

Linux 支持几种休眠状态:

状态 名称 功耗 唤醒延迟 说明
PM_SUSPEND_ON 正常工作 高 0 系统全速运行
PM_SUSPEND_STANDBY 待机 中 短 CPU暂停,外设保持供电
PM_SUSPEND_MEM 内存休眠 低 中 内存自刷新,CPU和外设断电
PM_SUSPEND_MAX 深度休眠 极低 长 取决于硬件支持

我在一个电池供电的IoT设备上,用的就是 PM_SUSPEND_MEM。系统大部分时间都在休眠,只有传感器触发时才唤醒。整机功耗从200mW降到了5mW,效果非常明显。

Suspend/Resume 的执行顺序很重要:

  • Suspend:先冻结用户进程,然后按设备树的依赖顺序,从叶子节点向根节点依次挂起。
  • Resume:反过来,从根节点向叶子节点依次恢复。

为什么这样设计?因为设备之间有依赖关系。比如I2C控制器必须先恢复,挂在其上的I2C设备才能正常工作。这个顺序搞反了,系统就起不来了。

4.5 设备树中的电源管理节点

设备树(Device Tree)是嵌入式Linux的标配。电源管理的配置,也写在设备树里。我见过不少工程师,代码写得挺好,设备树里电源管理节点却配得乱七八糟。

常用的电源管理属性:

  • power-domains:指定设备所属的电源域。
  • wakeup-source:标记设备可以作为唤醒源。
  • regulator-*:电源调节器相关属性。
  • clocks:时钟源,关闭时钟也是省电的重要手段。

举个例子,一个支持唤醒的触摸屏设备树节点:

&i2c1 {
    touchscreen@38 {
        compatible = "edt,edt-ft5406";
        reg = <0x38>;
        interrupt-parent = <&gpio1>;
        interrupts = <9 IRQ_TYPE_EDGE_FALLING>;
        wakeup-source;  /* 标记为唤醒源 */
        power-domains = <&pd_touch>;  /* 所属电源域 */
    };
};

这里 wakeup-source 是关键。没有它,触摸屏的中断无法唤醒系统。我曾经在一个项目里漏了这个属性,结果设备怎么按都唤不醒,排查了半天才发现是设备树的问题。

建议:在设备树里,每个可能作为唤醒源的设备,都要显式加上 wakeup-source。不要依赖默认值,不同内核版本的默认行为可能不一样。

4.6 sysfs与debugfs接口

调试电源管理,光看代码是不够的。sysfs 和 debugfs 提供了运行时接口,让我们能直接观察和控制系统状态。

sysfs 接口

sysfs 是用户空间和内核空间交互的主要通道。电源管理相关的 sysfs 节点主要集中在 /sys/power/ 和 /sys/devices/.../power/。

路径 作用 常用操作
/sys/power/state 控制系统休眠状态 echo mem > /sys/power/state
/sys/power/wakeup_count 查看/设置唤醒计数 用于处理并发唤醒事件
/sys/devices/.../power/control 控制设备的Runtime PM echo on/auto > control
/sys/devices/.../power/wakeup 查看/使能设备唤醒能力 echo enabled > wakeup
/sys/devices/.../power/autosuspend_delay_ms 设置自动挂起超时 echo 100 > autosuspend_delay_ms

我调试时最常用的命令:

# 查看系统支持的休眠状态
cat /sys/power/state

# 强制设备进入休眠
echo mem > /sys/power/state

# 查看某个设备的Runtime PM状态
cat /sys/devices/platform/soc/xxx/power/control

# 使能设备的唤醒功能
echo enabled > /sys/devices/platform/soc/xxx/power/wakeup

debugfs 接口

debugfs 提供了更详细的调试信息。需要内核开启 CONFIG_DEBUG_FS 和 CONFIG_PM_DEBUG。

常用的 debugfs 节点:

  • /sys/kernel/debug/pm_qos:查看PM QoS约束。
  • /sys/kernel/debug/suspend_stats:查看休眠/唤醒的统计信息。
  • /sys/kernel/debug/wakeup_sources:查看注册的唤醒源。

我记得有一次,系统总是莫名其妙地唤醒。用 cat /sys/kernel/debug/wakeup_sources 一看,发现是网卡的中断在频繁触发。后来在设备树里把网卡的 wakeup-source 去掉,问题就解决了。

注意:debugfs 在生产环境中建议关闭。它暴露了太多内核内部信息,有安全风险。调试完记得去掉 CONFIG_DEBUG_FS。

4.7 小结

Linux电源管理子系统,说白了就是一套「省电调度框架」。PM Core 定规矩,Runtime PM 管日常,Suspend/Resume 管大动作。设备树告诉内核硬件怎么接,sysfs 和 debugfs 让我们能看得见、摸得着。

这一章的内容,是后面所有低功耗设计的基础。把架构理清了,后面调驱动、调策略、调性能,才能有的放矢。