3、系统级休眠与唤醒:Suspend-to-RAM (S3)、Suspend-to-Idle (S0ix)、Hibernate (S4)、唤醒源与中断路由
好,咱们进入电源管理里最核心、也最让人头疼的一块——系统级休眠与唤醒。
说白了,就是让机器“睡过去”和“醒过来”。
你想想看,一个嵌入式设备,大部分时间其实都在待机。怎么让它睡得够沉、醒得够快,而且不出幺蛾子,这就是我们这章要聊的。
3.1 四种睡眠状态:从浅睡到深睡
ACPI规范定义了从S0到S5几种状态。我们做自动化测试,最常打交道的是S3、S0ix和S4。我习惯把它们理解成“打盹”、“假寐”和“冬眠”。
3.1.1 Suspend-to-RAM (S3) —— 经典“待机”
S3,也叫STR (Suspend-to-RAM)。
它的做法是:把CPU停了,把内存供电保持住,其他外设统统断电。
为什么?因为内存里存着你的系统状态。一唤醒,CPU直接从内存恢复执行,速度很快。
我在项目中遇到过一个坑:某款板子的DDR走线有问题,进入S3后内存自刷新失败,一唤醒就死机。后来我们专门加了一个S3循环测试,跑500轮才能复现。
测试要点:
- 进入S3后,测量内存VDD电压是否保持
- 唤醒后,检查dmesg是否有“Restarting tasks ... done”
- 重点关注USB、网卡等设备能否正常恢复
3.1.2 Suspend-to-Idle (S0ix) —— 现代“假寐”
S0ix是Intel平台搞出来的,后来ARM也跟进了类似机制。
它和S3最大的区别是:系统并没有真正“挂起”。操作系统还在跑,只是把CPU和大部分外设都塞进了极低功耗的 idle 状态。
说白了,就是“假装睡着了,但随时能醒”。
嗯,这里要注意:S0ix的功耗比S3高一点点,但唤醒延迟极低,适合需要快速响应的场景,比如手机待机。
我的调试经验:
我曾经为了验证一个S0ix的bug,连续跑了三天三夜的循环测试。最后发现是某个GPIO没有配置成唤醒源,导致系统无法进入最深度的S0ix子状态。用powertop一看,Residency全是0。
3.1.3 Hibernate (S4) —— 彻底“冬眠”
S4,休眠,也叫Suspend-to-Disk。
做法很粗暴:把整个内存镜像写到磁盘的swap分区里,然后彻底断电。
下次开机,从磁盘把镜像读回来,恢复到休眠前的状态。
好处是零功耗,坏处是慢——尤其是机械硬盘,读写几百MB甚至几个GB的数据,那叫一个煎熬。
避坑指南:
我曾经遇到过swap分区比内存小的情况,结果休眠时直接OOM。所以测试S4前,务必确认:swapon -s 显示的swap大小 > 内存大小。
3.2 唤醒源:谁把系统叫醒了?
系统睡过去了,总得有个东西把它叫醒。这个“东西”就是唤醒源。
常见的唤醒源有:
- RTC闹钟:定时唤醒,最常用。比如手机闹钟。
- GPIO中断:按键、传感器触发。
- USB唤醒:插拔USB设备,或者鼠标键盘活动。
- WOL (Wake-on-LAN):网卡收到特定网络包。
- 电源按钮:最原始的唤醒方式。
在Linux里,你可以通过 /sys/devices/.../power/wakeup 来查看和配置某个设备是否允许唤醒系统。
# 查看某个设备是否支持唤醒
cat /sys/class/rtc/rtc0/device/power/wakeup
# 启用唤醒
echo enabled > /sys/class/rtc/rtc0/device/power/wakeup
3.3 中断路由:唤醒的“最后一公里”
唤醒源产生了一个信号,这个信号怎么传到CPU,让CPU从睡梦中醒来?
这就涉及到中断路由了。
在S3状态下,大部分中断控制器(比如GIC、IOAPIC)还在工作,但只响应特定的唤醒中断。
在S0ix状态下,中断路由更复杂。因为CPU可能处于很深的C-state,需要把中断信号从外设一路“穿透”到CPU的电源管理单元。
我记得有一次,一个客户反馈说他们的设备无法通过GPIO唤醒。我查了半天,发现是GPIO控制器在休眠时被断电了,中断信号根本传不出去。后来在设备树里加了 wakeup-source 属性才解决。
自动化测试中如何验证中断路由?
- 进入休眠状态(S3/S0ix)
- 触发唤醒源(比如按一下按键)
- 检查系统是否在预期时间内唤醒
- 检查
/proc/interrupts中对应中断的计数是否增加 - 检查
dmesg中是否有“PM: resume of devices complete”
3.4 实战:一个简单的S3循环测试脚本
最后,分享一个我常用的测试脚本框架。自动化测试里,循环测试是发现偶发问题的利器。
#!/bin/bash
# S3循环测试脚本(简化版)
COUNT=100
FAIL=0
for ((i=1; i<=$COUNT; i++)); do
echo "=== S3 Cycle $i ==="
rtcwake -m mem -s 10
if [ $? -ne 0 ]; then
echo "FAIL: Cycle $i"
((FAIL++))
fi
sleep 2
done
echo "Total: $COUNT, Fail: $FAIL"
这个脚本用 rtcwake 工具,让系统进入S3睡眠10秒,然后自动唤醒。循环100次。
你想想看,如果某个驱动在唤醒时有资源泄漏,跑个几十轮可能就爆了。这种测试,手动做一次都嫌烦,但自动化跑起来,一晚上能跑几千轮。
小技巧:
我习惯在循环测试里同时监控 /sys/kernel/debug/wakeup_sources,看看每次唤醒是由哪个设备触发的。如果某次唤醒源不对,那肯定有问题。
好了,关于系统级休眠与唤醒,核心就是这些。记住:S3看内存,S0ix看中断路由,S4看swap。把这三点抓牢了,大部分问题都能定位。