1. 电源管理概述:为什么需要电源管理?

说实话,我刚开始接触Linux内核时,对电源管理这块是有点不屑的。总觉得这是硬件工程师该操心的事,软件写好了不就行了吗?直到有一次,我在一个嵌入式项目里,设备跑着跑着就过热降频,用户体验差得要命。老板问我:「你能不能让它凉快一点?」嗯,从那天起,我开始认真研究电源管理。

你想想看,现在的设备五花八门——手机、笔记本、服务器、IoT终端。它们对功耗的要求天差地别。手机没电了用户会骂娘,服务器电费太高运维会头疼,嵌入式设备散热不好直接死机。所以,电源管理不是「锦上添花」,而是「雪中送炭」。

为什么需要电源管理?

说白了,就三个核心诉求:

  • 省电:延长电池续航,降低运营成本
  • 散热:控制芯片温度,避免降频或损坏
  • 性能调优:在功耗和性能之间找到平衡点

我个人习惯把电源管理比作「开车」。你不能一直油门踩到底(高性能模式),也不能一直怠速(低功耗模式)。该加速时加速,该滑行时滑行,这才是老司机的做法。

核心观点:电源管理不是「省电」那么简单,它是系统级的资源调度艺术。

Linux电源管理架构全景图

Linux内核的电源管理,其实是一套「组合拳」。它不是一个单一的模块,而是多个子系统协同工作。我画了一张全景图,帮你理清脉络:

+---------------------------------------------------+
|                    用户空间                          |
|  (调频策略、休眠策略、电源管理工具)                  |
+---------------------------+-----------------------+
                            |
+---------------------------+-----------------------+
|                 内核电源管理核心                     |
|  +--------+  +--------+  +--------+  +--------+   |
|  |CPUFreq |  |CPUIdle |  |PM QOS  |  |Runtime |   |
|  |        |  |        |  |        |  |  PM    |   |
|  +--------+  +--------+  +--------+  +--------+   |
|  +--------+  +--------+                            |
|  |Suspend |  |Resume  |                            |
|  |/Resume |  |        |                            |
|  +--------+  +--------+                            |
+---------------------------+-----------------------+
                            |
+---------------------------+-----------------------+
|                    硬件层                            |
|  (CPU、设备、总线、时钟、电源域)                     |
+---------------------------------------------------+

这张图里,每个模块都有自己的职责。我来逐个拆解:

CPUFreq:动态调频调压

CPUFreq,全称是CPU Frequency Scaling。它的作用很简单——根据负载动态调整CPU的工作频率和电压。

我记得有一次,一个客户抱怨他们的服务器在低负载时功耗还是很高。我一看,CPU一直跑在最高频率,根本没降下来。问题出在哪?调频策略没配好。

CPUFreq支持多种调频策略:

策略名称行为描述适用场景
performance始终最高频率追求极致性能
powersave始终最低频率极致省电
ondemand按需调频,负载高时升频通用场景
conservative缓慢调频,避免频繁跳变对稳定性要求高的场景
schedutil基于调度器负载反馈调频现代内核推荐

我的建议:新项目直接用schedutil策略。它跟调度器配合更紧密,响应更及时。我踩过ondemand的坑——负载波动大时频率跳来跳去,反而增加了功耗。

CPUIdle:空闲时省电

CPUIdle跟CPUFreq是「兄弟俩」。CPUFreq管的是「干活时怎么省电」,CPUIdle管的是「不干活时怎么省电」。

CPU空闲时,可以进入不同的休眠状态(C-states)。C0是工作状态,C1是浅休眠,C2、C3越来越深。越深的休眠,省电越多,但唤醒延迟也越大。

我曾经在一个项目中,发现设备空闲时功耗还是偏高。排查了半天,发现是某个驱动阻止了CPU进入深休眠。嗯,这种问题很隐蔽,你得用powertop工具去抓。

# 查看CPU空闲状态统计
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/name
cat /sys/devices/system/cpu/cpu0/cpuidle/state*/time

PM QOS:性能与功耗的「裁判」

PM QOS(Quality of Service)是个很有意思的模块。它解决了一个核心矛盾:有的设备想省电,有的设备想要性能,听谁的?

举个例子:你在播放视频,GPU需要高性能,但CPU想降频省电。这时候PM QOS就出来当裁判了。它会收集所有设备的「需求」,然后取一个「最大公约数」——满足所有设备的最低要求。

注意:PM QOS的请求如果设置不当,会导致系统无法进入低功耗状态。我曾经见过一个驱动,在不需要高性能时还死死抓着高频率请求不放,结果整机功耗下不来。排查这种问题,你得看/sys/devices/system/cpu/cpu0/cpufreq/下的参数。

Runtime PM:运行时动态电源管理

Runtime PM,说白了就是「设备不用时就关掉」。它跟CPUIdle的思路类似,但作用在设备级别。

比如一个USB设备,长时间没有数据传输,就可以把它挂起。等有数据来了,再唤醒。这个过程对驱动开发者来说是透明的——内核帮你处理了大部分细节。

我建议你在写驱动时,尽量支持Runtime PM。它带来的功耗节省非常可观。尤其是那些「大部分时间闲着」的设备,比如触摸屏、传感器、网卡。

// Runtime PM的典型使用模式
static int my_probe(struct platform_device *pdev)
{
    pm_runtime_enable(&pdev->dev);
    pm_runtime_get_sync(&pdev->dev);
    // 初始化硬件...
    pm_runtime_put_sync(&pdev->dev);
    return 0;
}

Suspend/Resume:系统级休眠与唤醒

最后是Suspend/Resume,这是最「重」的电源管理手段。它把整个系统挂起到内存或磁盘,功耗降到最低。

Linux支持三种休眠模式:

  • Suspend-to-RAM (S3):挂起到内存,唤醒快,功耗低
  • Suspend-to-Disk (S4):挂起到磁盘,唤醒慢,几乎不耗电
  • Standby (S1):浅休眠,唤醒最快,省电有限

我记得有一次调试一个S3唤醒问题,设备休眠后怎么都唤不醒。查了三天,最后发现是某个GPIO的中断配置错了。嗯,这种问题最折磨人——硬件、软件、BIOS都有可能背锅。

避坑指南:调试Suspend/Resume问题时,先看dmesg日志,重点关注「PM: suspend entry」和「PM: resume exit」之间的输出。如果有驱动超时,它会打印「timed out」字样。

小结

好了,这一章我们走马观花地看了Linux电源管理的全貌。CPUFreq管频率、CPUIdle管空闲、PM QOS管协调、Runtime PM管设备、Suspend/Resume管系统。它们各司其职,又相互配合。

下一章,我会深入CPUFreq的源码实现,带你看看调频策略到底是怎么工作的。到时候我们会一起读代码,分析数据结构,甚至动手写一个简单的调频驱动。

记住一句话:电源管理不是「黑魔法」,它是有章可循的系统工程。你掌握了这些模块,就能在功耗和性能之间游刃有余。