4、系统挂起与休眠:Suspend-to-RAM (STR)、Suspend-to-Disk (STD)、hibernate流程分析
好,咱们今天聊点实在的。系统挂起和休眠,说白了就是让机器「睡过去」和「睡死过去」的区别。很多工程师觉得这俩差不多,其实差远了。我当年刚接触电源管理时,也以为 suspend 就是休眠,结果被项目里的功耗测试打脸了。
嗯,咱们先理清概念。Suspend-to-RAM(STR)就是挂起到内存,俗称「待机」。Suspend-to-Disk(STD)就是挂起到磁盘,也就是「休眠」。hibernate 这个词,其实和 STD 是一回事,只是不同内核版本叫法不同。
4.1 Suspend-to-RAM (STR) 流程
STR 的核心思路很简单:CPU 停了,内存还在供电。这样恢复时能秒醒。我做过一个平板项目,用户要求合盖就睡、开盖就醒,延迟不能超过 200ms。嗯,STR 是唯一的选择。
内核里,STR 的入口是 pm_suspend() 函数。它会走一套标准流程:
// 伪代码,简化版
pm_suspend(PM_SUSPEND_MEM) {
// 1. 通知所有设备:要睡了
dpm_suspend_start(PMSG_SUSPEND);
// 2. 暂停用户态进程
suspend_freeze_processes();
// 3. 关闭非启动 CPU
suspend_disable_secondary_cpus();
// 4. 架构相关的挂起操作
suspend_ops->enter(SUSPEND_MEM);
// 5. 醒来后反向恢复
suspend_enable_secondary_cpus();
suspend_thaw_processes();
dpm_resume_end(PMSG_RESUME);
}
这里有个关键点:suspend_ops->enter() 是平台相关的。x86 上会调用 ACPI 的 S3 状态,ARM 上则是调用 PSCI 的 CPU_SUSPEND。我踩过一个坑——某款 ARM 芯片的 PSCI 实现有 bug,suspend 后 GPIO 电平没保持住,导致外设掉电。后来加了个 suspend_late 阶段的回调才搞定。
STR 的关键特征:
- 内存保持供电,数据不丢失
- CPU 进入低功耗模式(WFI 或类似指令)
- 外设驱动需要实现 suspend/resume 回调
- 恢复时间通常在 100ms~1s 之间
我的经验:调试 STR 时,最烦的是某个驱动没处理好 suspend。我习惯在 dpm_suspend_start() 前后加 printk,看哪个设备卡住了。内核的 PM_DEBUG 选项一定要打开,能省很多时间。
4.2 Suspend-to-Disk (STD) 流程
STD 就狠多了。它把整个内存镜像写到磁盘,然后彻底断电。下次开机时,从磁盘把镜像读回来,恢复到挂起前的状态。说白了,就是「假关机」。
内核里,STD 的入口是 hibernate() 函数。流程比 STR 复杂得多:
// 伪代码,简化版
hibernate() {
// 1. 准备镜像:冻结进程、暂停设备
hibernation_snapshot();
// 2. 写入磁盘
swsusp_write();
// 3. 真正关机或进入 S4 状态
power_down();
// 4. 恢复时:从磁盘加载镜像
swsusp_read();
// 5. 恢复设备和进程
hibernation_restore();
}
这里有个细节:swsusp_write() 会把内存分成多个 chunk,每个 chunk 压缩后写入 swap 分区。我记得内核 4.x 之后默认用了 LZO 压缩,速度比之前的 zlib 快不少。但压缩率嘛……嗯,看情况。
注意:STD 要求 swap 分区足够大,至少得能装下整个内存。我见过有人配了 8GB 内存,swap 只给了 4GB,结果休眠时直接 OOM。另外,swap 分区不能是加密的,否则恢复时内核还没加载解密模块,读不了镜像。
4.3 hibernate 与 STD 的关系
其实 hibernate 就是 STD 的另一种叫法。内核代码里,hibernate() 和 pm_suspend(PM_SUSPEND_DISK) 最终走的是同一套逻辑。只是不同发行版、不同文档里叫法不同。
我个人习惯这样区分:
| 特性 | STR (Suspend-to-RAM) | STD / hibernate (Suspend-to-Disk) |
|---|---|---|
| 功耗 | 低(内存供电) | 零(完全断电) |
| 恢复速度 | 快(秒级) | 慢(取决于磁盘速度) |
| 数据安全 | 掉电丢失 | 掉电不丢失 |
| 适用场景 | 笔记本合盖、手机待机 | 台式机休眠、嵌入式低功耗 |
4.4 实际项目中的选择
做嵌入式产品时,选 STR 还是 STD,得看需求。我做过一个工业平板,要求「断电后恢复工作状态」。那只能上 STD,因为 STR 一断电就完蛋。但 STD 的恢复时间太长了,SSD 上也得 5~10 秒,用户抱怨「开机太慢」。后来折中了一下:正常待机用 STR,检测到电池电量低时自动切到 STD。
嗯,这里有个坑:切换策略要处理好。我曾经在代码里写了个定时器,每 5 分钟检查一次电量。结果电量低时触发 STD,但 STD 过程中又触发了另一个中断……死锁了。后来改成在 pm_suspend() 之前统一判断,才解决。
总结一下:
- STR:快,但怕掉电。适合短时间待机。
- STD:慢,但安全。适合长时间休眠或断电场景。
- hibernate:和 STD 一回事,别被名字搞混。
最后说一句:调试这些流程时,/sys/power/state 是你的好朋友。写个 echo mem > /sys/power/state 就能触发 STR,echo disk > /sys/power/state 触发 STD。配合串口日志,基本能定位 90% 的问题。剩下的 10%……嗯,那得看硬件了。