第三章 内核配置与编译:让电源管理功能“活”起来

好,咱们进入实战环节。前两章讲了理论,现在该动手了。配置内核支持电源管理,说白了就是告诉Linux内核:“嘿,你得管好这些硬件的用电。”

我个人习惯,拿到一块新板子,第一件事就是先把电源管理相关的选项过一遍。为什么?因为很多默认配置是“能用就行”,根本不管功耗。你想想看,一个嵌入式设备,电池就那么点容量,不把电源管理搞明白,产品怎么卖?

3.1 内核配置基础:从哪里入手?

配置内核,最常用的命令就是 make menuconfig。这个基于ncurses的图形界面,虽然看着有点复古,但用起来很顺手。我建议你养成一个习惯:每次修改配置前,先备份一份默认配置。

# 备份当前配置
cp .config .config.backup

# 进入配置界面
make menuconfig

电源管理相关的选项,主要集中在 Power management and ACPI options 这个菜单下。嗯,这里要注意,不同架构的芯片,这个菜单的入口可能略有不同。比如ARM架构下,可能叫 Power management options,没有ACPI那一套。

3.2 核心选项:必须开启的电源管理功能

咱们一个一个来看,哪些选项是必须勾上的。我按重要程度排个序,你照着配就行。

3.2.1 PM_SLEEP:睡眠功能的基础

这个选项是系统休眠和唤醒的基础。没有它,你的设备就别想睡个好觉。我曾经在一个项目中,发现系统怎么都进不了深度睡眠,查了两天,最后发现是内核编译时忘了开这个选项。你说冤不冤?

Power management and ACPI options --->
    [*] Suspend to RAM and standby
    [*] Hibernation (aka 'suspend to disk')

这里有两个子选项:Suspend to RAM(STR,挂起到内存)和 Hibernation(休眠到磁盘)。嵌入式设备一般用STR就够了,因为休眠到磁盘需要swap分区,很多小设备没这个条件。

3.2.2 PM_DEBUG:调试利器

这个选项,我强烈建议你在开发阶段打开。它会在内核日志里输出电源管理相关的调试信息。比如设备挂起失败时,会告诉你哪个驱动出了问题。

Power management and ACPI options --->
    [*] Power Management Debug Support
    [*]    Verbose Power Management debugging

打开后,你可以用 dmesg 查看详细的电源管理日志。举个例子:

# 查看电源管理相关的日志
dmesg | grep -i "suspend\|resume\|power"

不过要注意,生产环境里记得关掉这个选项。为什么?因为调试信息会消耗额外的CPU资源,而且日志刷得太快,反而影响性能。

3.2.3 CPU_FREQ:动态调频

CPU频率调节,这是电源管理的重头戏。Linux内核提供了 cpufreq 框架,可以根据系统负载动态调整CPU频率。说白了,就是“活多就快跑,活少就慢跑”。

CPU Power Management --->
    CPU Frequency scaling --->
        [*] CPU Frequency scaling
        [*]   CPU frequency transition statistics
        [*]   CPU frequency transition latency debugging

        Default CPUFreq governor (ondemand) --->
        <*>   'performance' governor
        <*>   'powersave' governor
        <*>   'userspace' governor
        <*>   'ondemand' governor
        <*>   'conservative' governor
        <*>   'schedutil' governor

这里有个小技巧:我一般会同时编译多个governor(调节器),然后在运行时根据场景切换。比如:

  • performance:最高频率,性能优先,适合跑基准测试
  • powersave:最低频率,省电模式
  • ondemand:按需调节,兼顾性能和功耗
  • schedutil:基于调度器的调节,最新的方案,效率更高

我个人习惯,默认用 schedutil,因为它能更精确地感知CPU负载变化。但要注意,这个governor需要内核支持 SCHED_DEADLINE 调度策略,老内核可能没有。

3.3 其他重要选项:别漏了这些

除了上面三个核心选项,还有一些容易被忽略的,我列个表给你看。

选项名称 功能描述 建议
PM_AUTOSLEEP 自动睡眠,系统空闲时自动进入低功耗状态 推荐开启
PM_WAKELOCKS 唤醒锁,防止系统在关键操作时进入睡眠 Android设备必开
CPU_IDLE CPU空闲管理,控制CPU在空闲时的状态 推荐开启
DEVFREQ 设备频率调节,类似cpufreq但用于外设 按需开启
REGULATOR 电压调节器框架,控制供电电压 硬件有regulator时必开

我记得有一次,一个同事调试功耗,发现设备待机时电流一直降不下来。查了半天,原来是 PM_AUTOSLEEP 没开,系统根本不会自动进入睡眠状态。开了之后,待机电流直接降了80%。

3.4 编译与部署:把配置变成能跑的内核

配置选好了,接下来就是编译。这一步其实没什么技术含量,但容易出错。我总结了一个标准流程:

# 1. 清理旧编译产物
make clean

# 2. 编译内核
make -j$(nproc)

# 3. 编译设备树(如果是ARM架构)
make dtbs

# 4. 编译内核模块
make modules

# 5. 安装内核模块
make modules_install INSTALL_MOD_PATH=/path/to/rootfs

# 6. 安装内核
make install

这里有个坑,我踩过好几次:make install 会把内核安装到当前系统的 /boot 目录下。如果你是在交叉编译环境里,一定要指定 INSTALL_PATH,否则会污染你的开发机。

警告:交叉编译时,务必使用 ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- 等参数。我曾经因为忘了指定架构,编译出来的内核在目标板上直接panic,查了半天才发现是x86的内核。

3.5 验证配置:怎么知道配对了?

编译部署完成后,怎么验证电源管理功能是否生效?我一般用这几个方法:

  1. 查看内核配置cat /proc/config.gz | gunzip | grep PM_,确认选项已开启
  2. 检查sysfs接口ls /sys/power/,看有没有 statewakeup_count 等文件
  3. 测试睡眠唤醒echo mem > /sys/power/state,看系统能否正常睡眠和唤醒
  4. 查看cpufreq信息cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_available_governors

嗯,这里要注意,有些开发板可能没有 /proc/config.gz,那就需要你在编译时开启 IKCONFIG_PROC 选项。

小技巧:我习惯在 .config 文件里搜索 =y=m 来确认选项状态。比如 grep "PM_SLEEP=y" .config,如果返回了结果,说明这个选项被编译进了内核。

3.6 避坑指南:我踩过的那些坑

做电源管理配置,有几个坑特别容易踩。我一个个说:

  • 坑一:设备树没配好。内核配置对了,但设备树里没定义电源管理相关的节点,比如 regulatorpower-domains,那系统照样没法管理电源。我曾经在一个项目里,设备树漏了 vcc-core 这个regulator,结果CPU核心电压一直降不下来。
  • 坑二:驱动不支持。有些外设驱动没有实现电源管理回调函数(suspend/resume),那系统睡眠时,这个设备就处于“失控”状态。我建议你编译时打开 PM_DEBUG,然后看日志里有没有 PM: Device xxx failed to suspend 这样的错误。
  • 坑三:governor选错了。默认的 ondemand 在有些场景下反应太慢,导致系统卡顿。我建议你试试 schedutil,它跟调度器配合得更好。

好了,这一章的内容就这些。配置内核支持电源管理,说白了就是“选对选项、编对代码、验对功能”。你按我说的步骤来,基本不会出大问题。下一章,咱们聊聊设备树里的电源管理节点,那才是真正考验硬件理解能力的地方。