> For the complete documentation index, see [llms.txt](https://freebsd-journal-cn.bsdcn.org/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://freebsd-journal-cn.bsdcn.org/20180304-zhuo-mian-bi-ji-ben/pain-and-suffering-on-the-road-to-resume.md).

# 唤醒之路上的痛与苦

作者：**John Baldwin**

## 唤醒之路上的痛与苦

如今大多数消费类计算设备都是移动设备。这些设备不固定连接电源插座，而是可以靠电池运行。移动设备上运行的操作系统的一项核心任务，就是在电池供电下最大化运行时间。达成这一目标的主要方式之一是关闭未使用的移动设备组件（例如关闭智能手机屏幕）。

目前 FreeBSD 无法在手机等几类移动设备上运行。但 FreeBSD 多年来能在另一类移动设备上运行：笔记本。传统上笔记本并没有非常精细的电源管理，但允许整个系统在不主动使用时进入低功耗状态。

将笔记本切换到低功耗状态称为“挂起”（suspend），将系统恢复到完全运行状态称为“唤醒”（resume）。FreeBSD 仅在基于 x86 的系统上支持挂起和唤醒。此外，虽然台式机和基于 x86 的笔记本都支持挂起和唤醒，但 FreeBSD 的开发工作只专注于笔记本。即便如此，FreeBSD 也只能在部分基于 x86 的笔记本上成功挂起和唤醒。

## x86 上的挂起与唤醒

基于 x86 的系统中挂起和唤醒支持经历了演化。

x86 上最早的系统级电源管理标准称为高级电源管理（Advanced Power Management，APM）。它支持两种低功耗状态：standby 与 suspend。standby 状态能比 suspend 状态更快回到完全运行状态，但 suspend 状态会关闭更多内部设备从而更省电。虽然操作系统可以控制单个设备的功耗，但这并非必须，BIOS 通常负责挂起时保存设备状态、唤醒时恢复保存的状态。FreeBSD 的 APM 支持依赖 BIOS 在挂起和唤醒期间管理各设备的电源。

APM 被高级配置与电源接口（Advanced Configuration and Power Interface，ACPI）取代。ACPI 是 APM 的超集，除电源管理外还包含若干用于设备管理的组件。ACPI 还采用了一组扩展的系统睡眠状态（见下表）。

| 睡眠状态 | 描述             |
| ---- | -------------- |
| S0   | 系统完全运行         |
| S1   | 不关闭任何组件        |
| S2   | 仅关闭 CPU        |
| S3   | 除 RAM 外所有设备都断电 |
| S4   | 所有设备断电，状态已保存   |
| S5   | 所有设备断电，状态丢失    |

ACPI 中的 S1 状态类似 APM 的 standby 状态，S3 状态类似 APM 的 suspend 状态。ACPI 还包含 S4 状态（即“休眠”），类似 S3，但 RAM 中的内容被保存到硬盘（或类似设备）上。从 S4 唤醒时需要将这份保存的副本加载回 RAM。最后，ACPI 增加了 S5 状态，允许操作系统关闭设备电源。配合这一变化，当用户按下电源按钮时 ACPI 会通知操作系统，给操作系统留出时间在系统断电前进行有序关机。

与 APM 相比，ACPI 要求主机操作系统在挂起和唤醒期间主动管理系统中许多设备的电源状态。例如，对于 S3 这样会断开设备电源的睡眠状态，操作系统需要为系统中所有 PCI 设备保存并恢复 PCI 配置空间寄存器值。

为推动休眠的应用，早期支持 ACPI S4 睡眠状态的系统提供了 BIOS 协助保存和恢复 RAM 内容的选项，称为 S4BIOS。当支持 S4BIOS 时，BIOS 会保存 RAM 内容（通常保存到 BIOS 拥有的专用硬盘分区），并在从 S4 唤醒时恢复 RAM 内容。当不使用 S4BIOS 时，操作系统负责在挂起期间将 RAM 内容保存到某种由操作系统管理的非易失性存储中。唤醒时，系统通电并按正常引导过程启动。操作系统引导过程需要识别出本次启动是从 S4 唤醒，定位已保存的 RAM 副本，并将其加载回 RAM 而不是执行正常引导。

## FreeBSD 的 ACPI 支持

虽然 FreeBSD 包含有限的 APM 支持，但现代 x86 系统大多只支持 ACPI。FreeBSD 首次通过 ACPI 支持挂起与唤醒是在 FreeBSD 5.0 的 i386 上。amd64（x86\_64）的支持首次出现在 FreeBSD 8.0。支持 S1 与 S3 睡眠状态，但许多笔记本只支持 S3，因为 S1 几乎不省电。FreeBSD 不支持原生 S4。在支持 S4BIOS 的系统上 FreeBSD 可以休眠，但现代笔记本不支持 S4BIOS。

## 系统控制节点

FreeBSD 在 `hw.acpi` 节点下创建了若干与挂起和唤醒相关的系统控制（**sysctl(8)**）节点。有些节点提供信息，有些用于控制挂起和唤醒行为。

定义列表格式如下：

**hw\.acpi.supported\_sleep\_state** 列出主机支持的 ACPI 睡眠状态。

**hw\.acpi.s4bios** 指示主机是否支持 S4BIOS。

**hw\.acpi.sleep\_delay** 在所有设备已挂起后、ACPI 驱动请求固件进入指定睡眠状态之前，挂起操作期间暂停的秒数。默认 1 秒。

**hw\.acpi.reset\_video** 可设为 1 以请求内核在唤醒期间使用遗留 BIOS 接口重置图形适配器。这在现代笔记本上通常无效，但在某些旧系统上能修复问题。默认 0（关闭）。

**hw\.acpi.standby\_state** 当用户态应用程序通过遗留 APM 接口请求切换到 APM“standby”状态时进入的 ACPI 睡眠状态。如果主机支持 S1，默认为 S1。

**hw\.acpi.suspend\_state** 当用户态应用程序通过遗留 APM 接口请求切换到 APM“suspend”状态时进入的 ACPI 睡眠状态。如果主机支持 S3，默认为 S3。

**hw\.acpi.lid\_switch\_state** 当用户合上笔记本盖子时进入的 ACPI 睡眠状态。默认 NONE。

**hw\.acpi.sleep\_button\_state** 当用户按下笔记本键盘上的挂起热键时进入的 ACPI 睡眠状态。默认为主机支持的最低挂起睡眠状态（S1 至 S4）。

**hw\.acpi.power\_button\_state** 当用户按下主机电源按钮时进入的 ACPI 睡眠状态。默认 S5。

在 Lenovo ThinkPad X220 上，启动后这些节点的初始值如下：

```sh
hw.acpi.supported_sleep_state: S3 S4 S5
hw.acpi.s4bios: 0
hw.acpi.sleep_delay: 1
hw.acpi.reset_video: 0
hw.acpi.standby_state: NONE
hw.acpi.suspend_state: S3
hw.acpi.lid_switch_state: NONE
hw.acpi.sleep_button_state: S3
hw.acpi.power_button_state: S5
```

这表明该系统支持 S3（挂起）、S4（原生休眠）和 S5（软关机）。按下挂起按钮会通过 S3 挂起。电源按钮触发优雅关机并断电。合上盖子不会执行任何操作。可通过将 `hw.acpi.lid_switch_state` 节点设为 S3 来配置笔记本在合上盖子时通过 S3 挂起：

```sh
# sysctl hw.acpi.lid_switch_state=S3
hw.acpi.lid_switch_state: NONE -> S3
```

## 用户态工具

FreeBSD 为用户态工具提供了请求系统挂起或关机的接口。窗口管理器小部件等第三方应用可以使用这些接口来实现用户发起的挂起。

一些基本系统工具也可请求睡眠状态切换。**shutdown(8)** 和 **halt(8)** 接受标志 `-p`，请求系统在干净关机后通过 S5 断电。**poweroff(8)** 是 `halt -p` 的别名。此外，**acpiconf(8)** 通过标志 `-s` 请求从 S1 到 S4 的睡眠状态切换。

## 设备驱动支持

设备驱动也负责在挂起和唤醒期间保存和恢复状态。总线驱动负责在系统挂起前为每个子设备保存总线定义的状态，并在唤醒时恢复。叶子设备驱动负责保存和恢复设备特定的状态。设备驱动还应在挂起前使活跃设备进入静止状态，并在唤醒时重启任何暂停的活动。两个需要显式支持挂起和唤醒的总线驱动是 ACPI 与 PCI 总线驱动。

ACPI 总线驱动主要管理子设备的电源状态和电源资源。在挂起请求期间，ACPI 总线驱动利用 ACPI 设备树中的信息，将任何支持低功耗状态的 ACPI 设备切换到固件指定的电源状态，同时为挂起做准备。这些设备在唤醒时恢复到全功率。此外，ACPI 设备树描述了电源生产者与消耗电源的设备之间的关系。如果在为挂起做准备时某个电源生产者下所有从其取电的设备都被关闭，ACPI 总线驱动会关闭这个电源生产者。唤醒时，ACPI 总线驱动会在恢复任何关联设备到全功率之前，先恢复对电源生产者的供电。

与 ACPI 总线驱动类似，PCI 总线驱动负责在为挂起做准备时将 PCI 设备置于低功耗状态（在可用时使用固件提示选择具体状态），并在唤醒时将设备恢复到全功率。与 ACPI 不同，PCI 总线不定义电源生产者与消费者关系。如果 PCI 设备依赖于独立的电源生产者，这种关系必须在 ACPI 设备树中描述，并由 ACPI 总线层管理。因此 ACPI 和 PCI 驱动在 PCI 设备的电源管理上分担一些共同责任。

与 ACPI 总线驱动不同，PCI 总线驱动还负责保存和恢复设备的标准化配置寄存器。在为系统挂起做准备时，PCI 总线驱动会为每个 PCI 设备在设备进入低功耗状态之前，对所有标准 PCI 配置寄存器（包括管理资源分配、中断路由和设备控制的寄存器）做一份快照（一旦设备进入低功耗状态，它就不再响应大多数配置寄存器的读写请求）。唤醒时，PCI 总线驱动会在设备恢复到全功率状态后恢复这些寄存器的值。

所有总线驱动（包括 ACPI 和 PCI）都会调用每个设备驱动中的两个方法，给叶子设备驱动保存和恢复设备特定状态的机会。`device_suspend` 方法由总线驱动在为系统挂起做准备时调用。`device_resume` 方法由总线驱动在唤醒期间调用。

`device_suspend` 方法由总线驱动在每个设备进入低功耗状态之前对其调用。设备驱动使用此方法保存设备特定寄存器的副本，并暂停当前活动、禁用任何活跃中断。例如，网卡驱动会禁用接收器并禁用任何中断。此外，如果设备能够在挂起期间唤醒系统（且管理员通过 **ifconfig(8)** 为特定网卡请求了 WOL），挂起方法应启用这一功能。某些网卡允许通过称为 Wake On LAN（WOL）的特殊网络包唤醒系统。

`device_resume` 方法由总线驱动在每个设备恢复到全功率状态之后对其调用。设备驱动使用此方法恢复任何设备特定寄存器，并恢复之前暂停的活动。支持唤醒功能的设备可能还需要禁用该功能。对网卡驱动而言，唤醒方法通常会重启任何待处理数据包的传输，并启用网卡接收器。

## 调试挂起与唤醒

尽管 FreeBSD 在挂起与唤醒上做了大量工作，但目前挂起与唤醒只能在一组有限的系统上工作。不幸的是，单个设备驱动的问题就可能导致整个系统的挂起和唤醒失败。

诊断挂起或唤醒失败的原因相当困难。通常挂起或唤醒失败时，笔记本就挂在那里，屏幕一片漆黑。由于屏幕已关闭，无法通过控制台显示的消息来缩小失败原因的范围。如果笔记本有非 USB 的串口，则可以通过串口记录消息（例如使用串行控制台）。然而现代笔记本一般不配备串口。一些笔记本提供虚拟串口（例如通过 Intel 的 AMT），但根据作者经验，这些串口在失败的唤醒尝试中无法可靠地恢复到可用状态。

有时笔记本会大致唤醒成功，但屏幕仍然关闭。此时键盘仍然响应输入，如果是在单用户模式下挂起的，可以盲打命令来判断笔记本是否处于这种状态。例如，在此状态下键入 `poweroff` 并回车，应能听到硬盘活动激增，几秒钟后机器关机。这种特定的失败模式实际上是作者经验中最常见的唤醒失败类型。如果笔记本的图形适配器由 GPU 驱动（如 `i915kms.ko`）支持，那么在挂起前加载此驱动通常能解决这种特定的失败情形。

调试其他一些挂起和唤醒失败的更多建议可见 <https://wiki.freebsd.org/SuspendResume>。

## 未来方向

挂起和唤醒当然还有改进和未来工作的空间。两大主要工作方向是支持 ACPI 的 S4 状态（休眠）和 ACPI 的全新低功耗空闲状态。

FreeBSD 尚不支持原生 S4 或休眠。与 S3 挂起模式不同，从 S4 唤醒在恢复操作系统之前会遵循正常的上电过程。这使硬件处于类似初始启动的状态，依赖固件引导将设备初始化到已知状态。这应使从 S4 唤醒不那么容易出现设备特定问题，例如无法恢复屏幕供电。与 S3 相比，休眠还能节省更多电力。在 S3 挂起期间，笔记本仍会消耗电力。如果电池在 S3 期间耗尽，内存中保存的状态就会丢失。而 S4 在挂起期间不耗电，可无限期地保持挂起。除支持原生 S4 外，FreeBSD 还应更好地支持挂起和唤醒策略管理，例如笔记本电量低时自动挂起。

除休眠支持外，FreeBSD 的下一个主要挑战将是支持 ACPI 全新的低功耗空闲状态。ACPI 6.0 引入了新的系统电源管理模型。不再使用单一的系统级睡眠状态，低功耗空闲状态鼓励对设备和处理器进行更细粒度的电源控制。在此模型中，操作系统通过尽可能让设备和处理器处于低功耗状态来最小化功耗。此外，在响应唤醒事件（如 WOL 包）时，操作系统应只恢复系统中处理该特定事件所需组件的电源，让其他组件保持在低功耗状态。虽然当前系统仍支持 ACPI 的传统系统级睡眠状态，但预期低功耗空闲状态最终将在笔记本和台式机等系统上取代系统睡眠状态。这需要在 FreeBSD 设备驱动基础设施中更全面地理解电源使用以及电源生产者/消费者关系，并至少探索线程调度的替代方案。•

***

**JOHN BALDWIN** 于 1999 年作为 committer 加入 FreeBSD 项目。他在系统的多个领域工作过，包括 SMP 基础设施、网络协议栈、虚拟内存和设备驱动支持。John 曾任职于 Core 和发布工程团队，并组织过多次 FreeBSD 开发者峰会。


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://freebsd-journal-cn.bsdcn.org/20180304-zhuo-mian-bi-ji-ben/pain-and-suffering-on-the-road-to-resume.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
