跳转至内容

电源管理/挂起与休眠

来自 ArchWiki
(重定向自 Suspend)

多种挂起方法 可用,特别是

挂起到空闲 (Suspend to idle)
在 Intel 被称为 S0ix,在 Microsoft 被称为 Modern Standby(先前称为“Connected Standby”),在内核中称为 S2Idle。对于支持的系统,它旨在取代 **S3** 睡眠状态,提供相同的节能效果,但唤醒时间大大缩短。
提示 虽然此状态在 WindowsmacOS 上存在电池耗尽问题,因为它们支持在此状态下唤醒设备以进行网络活动,但 Linux 软件生态系统目前不使用此功能,应不受影响。
挂起到内存 (Suspend to RAM,也称挂起)
ACPI 定义的 **S3** 睡眠状态。通过切断除 RAM 以外的大部分硬件电源来工作,RAM 用于恢复机器的状态。由于节能效果显著,建议笔记本电脑在运行时自动进入此模式,当设备在电池供电且盖子关闭(或用户一段时间不活动)时。
挂起到硬盘 (Suspend to disk,也称休眠)
ACPI 定义的 **S4** 睡眠状态。将机器的状态保存到 交换空间 并完全关闭机器。当机器重新启动时,状态将被恢复。在此之前,有 功耗。
混合挂起 (Hybrid suspend,也称混合睡眠)
挂起和休眠的混合体,有时也称为 **挂起到两者**。将机器的状态保存到交换空间,但不关闭机器。相反,它会调用默认的挂起。因此,如果电池未耗尽,系统可以立即恢复。如果电池耗尽,系统可以从磁盘恢复,这比从内存恢复慢得多,但机器的状态没有丢失。

内核提供基本功能,一些高级接口提供微调功能以处理有问题的硬件驱动/内核模块(例如,显卡重新初始化)。

内核接口 (swsusp)

注意 虽然可以直接使用内核接口,因为这样更快,无需额外的用户空间处理,但建议使用 高级接口。它做得更好,因为它执行额外的安全检查,并提供挂起前后的钩子机制,以便正确设置硬件时钟、恢复无线网络等。

可以直接通知内核软件挂起代码 (swsusp) 进入挂起状态;确切的方法和状态取决于硬件支持级别。在现代内核中,向 /sys/power/state 写入适当的字符串是触发此挂起的首选机制。

有关详细信息,请参阅 内核文档

高级接口 (systemd)

注意 高级接口的目标是提供可调用的二进制文件/脚本来执行挂起/休眠,并为上述过程提供钩子以进行额外的准备/清理工作。有关电源按钮、菜单点击或笔记本盖子事件自动进入睡眠状态,请参阅 电源管理#ACPI 事件

systemd 为挂起、休眠和混合挂起提供了原生命令。这是 Arch Linux 中使用的默认接口。

systemctl suspend 应该开箱即用。要使 systemctl hibernate 在您的系统上正常工作,您可能需要按照 #休眠 中的说明进行操作。

还有两种混合挂起和休眠的模式

  • systemctl hybrid-sleep 将系统同时挂起至内存和硬盘,因此完全断电不会导致数据丢失。此模式也称为 *挂起到两者*。
  • systemctl suspend-then-hibernate 首先将系统尽可能长时间地挂起到 RAM,然后通过 RTC 闹钟唤醒系统并进入休眠。RTC 闹钟由 systemd-sleep.conf(5) 中的 HibernateDelaySec 设置。默认值通过估算电池放电速率来设置,以使系统保留 5% 的电池电量,或者在没有电的情况下维持两个小时。此估算值是通过在 systemd-sleep.conf(5) 中由 SuspendEstimationSec 指定的时间后电池电量的变化来获得的,此时系统会短暂唤醒以进行测量(当系统从挂起状态手动唤醒时也会进行一次测量)。
提示 systemd v256 通过新的 systemctl sleep 命令支持自动选择最合适的睡眠操作。默认情况下,使用 suspend-then-hibernate,如果不支持,则回退到 suspend,然后是 hibernate。有关更多信息,请参阅 systemctl(1)

有关配置挂起/休眠钩子的更多信息,请参阅 #睡眠钩子。另请参阅 systemctl(1)systemd-sleep(8)systemd.special(7)

更改挂起方法

在 S0ix 挂起节能效果不如常规 S3 睡眠的系统上,或者在优先节能而非快速恢复时间的情况下,可以更改默认的挂起方法。

提示 S0ix 旨在提供与 S3 睡眠相同或更好的节能效果。请参阅 Intel 的博客文章 Linux 中的 S0ix 状态实现方法Linux S0ix 故障排除Linux 高效空闲:案例研究,以检查您是否能使其按预期工作。Intel 系统上的用户可以使用 S0ixSelftestTool。AMD 系统上的用户可以使用 amd-debug-tools

运行以下命令以查看硬件支持的所有挂起方法(当前方法显示在方括号中[1]

$ cat /sys/power/mem_sleep
[s2idle] shallow deep
内存睡眠支持的状态
mem_sleep 字符串 睡眠状态
s2idle 挂起到空闲
shallow standby
deep 挂起到内存

如果您的硬件未报告 deep 睡眠状态,请先检查您的 UEFI 是否报告了相关设置,通常在“电源”或“睡眠状态”等选项下,其选项名称可能为“Windows 10”、“Windows and Linux”或“S3/Modern standby support”(用于 S0ix),以及“Legacy”、“Linux”、“Linux S3”或“S3 enabled”(用于 S3 睡眠)。如果不行,您可以继续使用 s2idle,考虑使用 休眠 或尝试修补 DSDT 表(或在线查找修补版本)。

注意 最后的解决方案很可能会引起问题。制造商已停止修复 ACPI S3 状态的错误,因为默认情况下,出货到 Windows 的系统鼓励使用“Modern standby”;如果它们自愿未报告该状态,则很可能在某种程度上存在问题。

更改睡眠方法后,通过测试几次睡眠周期来确认您的硬件在 S3 睡眠方面没有出现问题。

# echo deep > /sys/power/mem_sleep

如果没有发现问题,您可以通过 systemd-sleep.conf(5) 中的 MemorySleepMode 指令永久更改。

/etc/systemd/sleep.conf.d/mem-deep.conf
[Sleep]
MemorySleepMode=deep

或者通过 内核参数 mem_sleep_default=deep

在某些相反的情况下,有问题的固件声称支持 deep 睡眠,而实际上只支持 s2idle。在这种情况下,可以通过 SuspendState 设置来使用 s2idle 的替代方法。

/etc/systemd/sleep.conf.d/freeze.conf
[Sleep]
SuspendState=freeze

休眠

为了使用休眠,您必须创建一个 交换 分区或文件,配置 initramfs 以便在早期用户空间启动恢复过程,并以 initramfs 可用的方式指定交换空间的位置,例如由 systemd 定义的 HibernateLocation EFI 变量或 resume= 内核参数。这三个步骤将在下面详细介绍。

关于交换分区/文件大小

即使您的交换分区小于 RAM,您仍有很大的机会成功休眠。有关 image_size sysfs(5) 伪文件的信息,请参阅内核文档中的“image_size”:kernel documentation

您可以减小 /sys/power/image_size 的值,使挂起镜像尽可能小(适用于较小的交换分区),或者增大它以可能加速休眠过程。对于拥有大量 RAM 的系统,较小的值可能会显著加快恢复休眠系统的速度。 systemd#systemd-tmpfiles - 临时文件 可用于使此更改持久化。

/etc/tmpfiles.d/hibernation_image_size.conf
#    Path                   Mode UID  GID  Age Argument
w    /sys/power/image_size  -    -    -    -   0

挂起镜像不能跨越多个交换分区和/或交换文件。它必须完全包含在一个交换分区或一个交换文件中。[2]

配置 initramfs

  • 当使用带有 systemd 钩子的 initramfs 时(这是默认设置),已经提供了恢复机制,无需添加其他钩子。
  • 在使用基于 busybox 的 initramfs 时,需要在 /etc/mkinitcpio.conf 中添加 resume 钩子。无论是通过标签还是 UUID,交换分区都通过 udev 设备节点引用,因此 resume 钩子必须在 udev 钩子 *之后*。此示例是从默认钩子配置开始的(在 systemd 钩子成为默认之前)
HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems resume fsck)
请记住 重新生成 initramfs 以使这些更改生效。
注意 如果为交换空间使用了堆叠存储(例如 dm-cryptRAIDLVM),则最终映射的设备必须在早期用户空间可用,并且在启动恢复过程之前。即,在这些设置中,resume 钩子必须放在 encryptlvm2 等钩子之后。

将休眠位置传递给 initramfs

当系统休眠时,内存镜像会被转储到交换空间,其中也包括挂载的文件系统的状态。因此,必须将休眠位置提供给 initramfs,即在挂载根文件系统之前,以便从休眠中恢复。

当系统在 UEFI 上运行时,systemd-sleep(8) 会自动选择一个合适的交换空间进行休眠,并且使用的交换空间的信息存储在 HibernateLocation EFI 变量中。下次启动时,systemd-hibernate-resume(8) 会从 EFI 变量读取位置,系统便会恢复。这意味着以下步骤并非必需,除非系统使用的是旧式 BIOS 或您想选择一个不同于自动选择的交换空间。

手动指定休眠位置

可以使用 内核参数 resume=swap_device,其中 swap_device 遵循 持久块设备命名。例如

  • resume=UUID=4209c845-f495-4c43-8a03-5363dd433153
  • resume="PARTLABEL=Swap partition"
  • resume=/dev/archVolumeGroup/archLogicalVolume – 如果交换分区位于 LVM 逻辑卷上(UUID 和 Label 也应该可用)
注意 对于堆叠块设备,例如加密容器(LUKS)、RAID 或 LVM,resume= 参数必须指向未加密/映射的设备(如果使用了交换文件,它包含带有交换文件的文件系统;另请参阅下面的提示)。

内核参数仅在重启后生效。要立即休眠,请从 lsblk 获取卷的主次设备号,并以 major:minor 的格式将其 echo 到 /sys/power/resume

例如,如果交换设备是 8:3

# echo 8:3 > /sys/power/resume

如果使用交换文件,请另外遵循 #获取交换文件偏移量 中的步骤。

获取交换文件偏移量

当使用 交换文件 进行休眠时,包含文件系统的块设备应在 resume= 中指定,并且此外,必须通过 resume_offset= 内核参数 指定交换文件的物理偏移量。[3]

在 Btrfs 以外的文件系统上,可以通过运行 filefrag -v swap_file 来获取 resume_offset= 的值。输出是表格格式,所需值在 physical_offset 列的第一行。

例如:

# filefrag -v swap_file
Filesystem type is: ef53
File size of swap_file is 4294967296 (1048576 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:      38912..     38912:      1:
   1:        1..   22527:      38913..     61439:  22527:             unwritten
   2:    22528..   53247:     899072..    929791:  30720:      61440: unwritten
...

在示例中,resume_offset= 的值为第一个 38912

或者,要直接获取偏移量值

# filefrag -v swap_file | awk '$1=="0:" {print substr($4, 1, length($4)-2)}'

对于 Btrfs,请不要尝试使用 filefrag 工具,因为从 filefrag 获得的“物理”偏移量并不是磁盘上的实际物理偏移量;存在一个虚拟磁盘地址空间以支持多个设备。[4] 相反,请使用 btrfs-inspect-internal(8) 命令。例如

# btrfs inspect-internal map-swapfile -r swap_file
198122980

在此示例中,内核参数将是 resume_offset=198122980

要立即应用更改(无需重启),请将恢复偏移量 echo 到 /sys/power/resume_offset。例如,如果偏移量是 38912

# echo 38912 > /sys/power/resume_offset
提示 以下命令可用于识别交换文件的 backing device:findmnt -no UUID -T swap_file

更改休眠的镜像压缩算法

从 Linux 6.9 开始[5],可以更改休眠的镜像压缩算法。默认压缩算法根据编译选项 CONFIG_HIBERNATION_DEF_COMP 选择,但可以在启动时和运行时覆盖。

不同的压缩算法具有不同的特性,当休眠使用任何这些算法时,可能会受益,特别是当次要算法(LZ4)比默认算法(LZO)具有更好的解压缩速度时,这反过来又会缩短休眠镜像的恢复时间。

您可以通过两种方式覆盖默认算法

1) 将 hibernate.compressor 作为 内核参数 传递

hibernate.compressor=lzo
hibernate.compressor=lz4

2) 在运行时指定算法

# echo lzo > /sys/module/hibernate/parameters/compressor
# echo lz4 > /sys/module/hibernate/parameters/compressor

目前支持 lzolz4 算法,其中 LZO 是默认设置。

使用 zram 维护休眠的交换文件

提示 与涉及多个交换空间的以下设置相比,可以使用 zswap 来实现类似的行为。

通过同时维护两个或多个交换空间,可以解决 zram RAM-only 交换的休眠问题。在触发休眠之前,systemd 始终会忽略 zram 块设备[6],因此保持两个空间启用应该无需进一步干预。

配置交换文件 后,请遵循 zram 页面。确保 zram 具有 **更高的交换优先级**(例如 pri=100)。

休眠到精简配置的 LVM 卷

休眠到精简配置的 LVM 卷是可能的,但您必须确保该卷已完全分配。否则,从中恢复将失败,请参阅 FS#50703

您可以通过简单地用零填充 LVM 卷来完全分配它。例如

# dd if=/dev/zero of=/dev/vg0/swap bs=1M status=progress

要验证卷是否已完全分配,您可以使用

# lvs
  LV                   VG  Attr       LSize   Pool Origin    Data%  Meta%  Move Log Cpy%Sync Convert
  swap                 vg0 Vwi-aot--- 10.00g  pool           100

完全分配的卷将显示为 100% 的数据使用率。

警告 不要对用于休眠的精简配置的交换卷使用 TRIM,即不要在 /etc/fstab 中使用 discard,也不要使用 swapon-d/--discard 选项。否则,已使用的空间将被释放。

禁用 zswap 回写,仅将交换空间用于休眠

在 Linux 6.8 中,zswap 获得了一个 每个 cgroup 的禁用回写选项。通过使用 systemd unit 设置 MemoryZSwapWriteback(参阅 systemd.resource-control(5) § 内存会计与控制),在所有可能的 unit 类型中,可以有效地完全禁用 zswap 回写。这使得 zswap 可以像 zram 一样使用,并增加了支持休眠的好处。

为了避免手动创建十二个顶层每种类型的 drop-in 文件(用于系统和用户 scopeserviceslicesocketmountswap unit 类型),请 安装 zswap-disable-writebackAUR启用 zswap 并重启以使设置生效。

尝试执行内存密集型任务,并确认 zswap 没有向磁盘写入任何内容

# cat /sys/kernel/debug/zswap/written_back_pages
0

睡眠钩子

自定义 systemd 单元

systemd 分别为挂起、休眠、混合挂起和挂起后休眠目标启动 suspend.targethibernate.targethybrid-sleep.targetsuspend-then-hibernate.target。所有上述目标都将 sleep.target 拉入。可以使用任何目标在挂起/休眠之前或之后调用 自定义单元。应为用户操作和 root/系统操作创建单独的文件。示例:

/etc/systemd/system/user-suspend@.service
[Unit]
Description=User suspend actions
Before=sleep.target

[Service]
User=%I
Type=forking
Environment=DISPLAY=:0
ExecStartPre= -/usr/bin/pkill -u %u unison ; /usr/local/bin/music.sh stop
ExecStart=/usr/bin/sflock
ExecStartPost=/usr/bin/sleep 1

[Install]
WantedBy=sleep.target
/etc/systemd/system/user-resume@.service
[Unit]
Description=User resume actions
After=suspend.target hibernate.target

[Service]
User=%I
Type=simple
ExecStart=/usr/local/bin/ssh-connect.sh

[Install]
WantedBy=suspend.target hibernate.target

启用 user-suspend@user.service 和/或 user-resume@user.service 以使更改生效。

注意 由于屏幕锁定程序可能在屏幕“锁定”之前返回,屏幕可能会在从挂起恢复时闪烁。通过 ExecStartPost=/usr/bin/sleep 1 添加一个小的延迟有助于防止这种情况。

对于 root/系统操作:

/etc/systemd/system/root-suspend.service
[Unit]
Description=Local system suspend actions
Before=sleep.target

[Service]
Type=simple
ExecStart=-/usr/bin/pkill sshfs

[Install]
WantedBy=sleep.target
/etc/systemd/system/root-resume.service
[Unit]
Description=Local system resume actions
After=suspend.target hibernate.target

[Service]
Type=simple
ExecStart=/usr/bin/systemctl restart mnt-media.automount

[Install]
WantedBy=suspend.target hibernate.target

合并睡眠/恢复单元

使用合并的单元文件,一个钩子可以完成不同阶段(睡眠/恢复)和不同目标的全部工作。

示例和说明

/etc/systemd/system/wicd-sleep.service
[Unit]
Description=Wicd sleep hook
Before=sleep.target
StopWhenUnneeded=yes

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=-/usr/share/wicd/daemon/suspend.py
ExecStop=-/usr/share/wicd/daemon/autoconnect.py

[Install]
WantedBy=sleep.target
  • RemainAfterExit=yes: 启动后,服务被视为活动状态,直到被明确停止。
  • StopWhenUnneeded=yes: 活动时,如果没有任何其他活动服务需要它,则会停止该服务。在此特定示例中,它将在 sleep.target 停止后停止。
  • 由于 sleep.target 设置了 StopWhenUnneeded=yes,因此保证了钩子能够为不同的任务正确启动/停止。

/usr/lib/systemd/system-sleep 中的钩子

注意 systemd 根据 systemd-sleep(8) 的说法,认为此方法是一种 hack。systemd-sleep 会并发运行这些钩子,而不是一个接一个地运行。有关更明确的、支持排序的接口,请参阅 #自定义 systemd 单元

systemd-sleep 运行 /usr/lib/systemd/system-sleep/ 中的所有可执行文件,并将两个参数传递给每个可执行文件

  1. prepost,取决于机器是即将进入睡眠还是正在唤醒。
  2. suspendhibernatehybrid-sleepsuspend-then-hibernate,取决于正在调用的操作。

名为 SYSTEMD_SLEEP_ACTION 的环境变量将被设置,其中包含正在处理的睡眠操作。这主要对于 suspend-then-hibernate 有用,在这种情况下,变量的值将是 suspendhibernate,或者在休眠失败的情况下是 suspend-after-failed-hibernate

任何自定义脚本的输出都将被 systemd-suspend.servicesystemd-hibernate.servicesystemd-hybrid-sleep.service 记录。您可以在 systemdjournalctl 中看到其输出。

# journalctl -b -u systemd-suspend.service

自定义睡眠脚本示例

/usr/lib/systemd/system-sleep/example.sh
#!/bin/sh
case $1/$2 in
  pre/*)
    echo "Going to $2..."
    ;;
  post/*)
    echo "Waking up from $2..."
    ;;
esac

不要忘记使您的脚本 可执行

技巧与提示

在受信任的位置自动解锁

恢复时,如果系统连接到特定设备或受信任的 Wi-Fi 网络,您可以自动解锁系统。

/etc/local-scripts/resume-unlock.sh
#!/usr/bin/bash
# Unlock session if at a trusted location

function trusted() {
    # Check if connected to a trusted Wi-Fi network
    [[ $(iwgetid -r) == your_home_ssid ]] \
        && return 0

    # Check if trusted USB device is connected.
    #lsusb -d xxxx:xxxx && return 0

    return 1 # Not trusted
}

for (( i=0; i < 10; i++ )); do
    if trusted; then
        loginctl unlock-sessions
        exit
    fi
    sleep 0.5
done

配置您的桌面环境使其在恢复时锁定,然后创建一个 睡眠钩子,在恢复后运行上述脚本。您还需要 安装 wireless_tools 来读取已连接的 Wi-Fi SSID。如果您还想测试已连接的 USB 设备,请取消脚本中 lsusb -d ... 行的注释,并填入您受信任设备的 ID。您可以通过运行 lsusb 来获取设备的 ID。

完全禁用睡眠

当使用设备作为服务器等用途时,可能不需要挂起/休眠,甚至可能不希望这样做。每个睡眠状态都可以通过 systemd-sleep.conf(5) 禁用。

/etc/systemd/sleep.conf.d/disable-sleep.conf
[Sleep]
AllowSuspend=no
AllowHibernation=no
AllowHybridSleep=no
AllowSuspendThenHibernate=no

Intel 快速启动技术 (IRST)

Intel 快速启动技术是一种休眠的固件方法,它允许在预定义间隔后或根据电池状态从睡眠状态休眠。这应该比常规休眠更快、更可靠,因为它由固件而不是操作系统执行。通常它必须在固件中启用,并且固件还支持在挂起/电池事件触发休眠后设置持续时间。但是,一些设备——尽管在固件中支持 IRST——但只能通过 Intel 的 Windows 驱动程序进行配置。在这种情况下,下面描述的 intel-rst 内核模块应该能够在 Linux 下配置事件。

启用 Intel 快速启动技术 (IRST) 后,从深度睡眠中恢复需要“比从 S3 恢复慢几秒钟,但比从休眠恢复快得多”。

许多基于 Intel 的系统具有 IRST 的固件支持,但需要 SSD(而不是 HDD)上的特殊分区。Windows 的 OEM 部署可能有一个预先存在的 IRST 分区,可以在 Arch Linux 安装过程中保留(而不是擦除并重新分区整个 SSD)。它应该显示为一个大小等于系统 RAM 的未格式化分区。

警告 Intel 快速启动分区未加密;“Intel 建议在您使用基于软件的磁盘加密时禁用 Intel 快速启动技术”。[7]

如果您打算擦除并重新分区整个驱动器(或已经这样做),那么如果您还打算使用该技术,则必须重新创建 IRST 分区。这可以通过创建一个大小等于系统 RAM 的空分区来完成,并将其分区类型设置为 GUID D3BFE2DE-3DAF-11DF-BA40-E3A556D89593(用于 GPT 分区)或 ID 0x84(用于 MBR 分区)。您可能还需要在系统固件设置中启用 IRST 支持。

提示 IRST 在(挂起后)启动前的持续时间可以在系统固件设置中调整。

IRST 休眠过程的时间(即,将“整个 RAM 内容复制到一个特殊分区”)取决于系统的 RAM 大小和 SSD 速度,因此可能需要 20-60 秒。一些系统可能会通过 LED 指示灯表明过程的完成,例如当指示灯停止闪烁时。

在 Linux 内核中配置 IRST 休眠事件需要将 CONFIG_INTEL_RST 内建或作为模块编译。一旦通过 modprobe intel_rst 加载,它应该会在 /sys/bus/acpi/drivers/intel_rapid_start/*/ 下创建 wakeup_eventswakeup_time 文件,可用于进一步配置。该模块的文档很少,请参阅源代码 drivers/platform/x86/intel/rst.c 以获取更多详细信息。

另请参阅 Intel 快速启动技术的 通用问答用户指南

在睡眠状态下跟踪笔记本电脑电池电量变化

要测量挂起状态下的功耗,请使用 Batenergy 脚本将电池变化记录到系统日志。这允许比较 S3 / S0x 状态下的功耗,或者在 BIOS 和内核更新后检查回归和修复。该脚本需要安装 bc 用于计算。

故障排除

ACPI_OS_NAME

您可能需要调整您的DSDT表以使其正常工作。请参阅 DSDT

挂起/休眠不起作用,或工作不一致

本文或本章节的准确性存在争议。

原因:本节混合了几个不相关的起因。(请在 Talk:Power management/Suspend and hibernate 中讨论)

有许多报告称在挂起/休眠过程中或恢复时屏幕变黑,没有易于查看的错误信息,也无法进行任何操作。笔记本电脑和台式机都出现了这些问题。这不是官方解决方案,但切换到旧内核,特别是 LTS 内核,可能会解决这个问题。

在使用硬件看门狗定时器时可能会出现问题(默认禁用,请参阅 systemd-system.conf(5) § OPTIONS 中的 RuntimeWatchdogSec=)。有缺陷的看门狗定时器可能会在系统完成休眠映像创建之前重置计算机。

有时屏幕变黑是由于 initramfs 中的设备初始化引起的。删除 Mkinitcpio#MODULES 中可能存在的任何模块,删除 kms hook 并重新构建 initramfs 可能会解决此问题,特别是对于 早期 KMS 的图形驱动程序。在恢复之前初始化这些设备可能导致不一致,从而阻止系统从休眠中恢复。这不会影响从 RAM 恢复。另外,请查看博客文章 调试挂起问题的最佳实践

ATI 显卡驱动程序切换到较新的 AMDGPU 驱动程序也有助于使休眠和唤醒过程成功。

对于 NVIDIA 显卡,VRAM 内容会在挂起时保存到磁盘。[8] 确保您有足够的磁盘空间,否则恢复时可能会出现黑屏。另一个可能导致此问题的原因是通过 黑名单 模块 nvidiafb 来修复。[9]

使用 intel_lpss_pci 模块进行触摸板的 Intel CPU 笔记本电脑可能会在恢复时遇到内核恐慌(大写锁定键闪烁)[10]。该模块需要被添加到 initramfs 中,如下所示:

/etc/mkinitcpio.conf
MODULES=(... intel_lpss_pci ...)

然后重新生成 initramfs

USB 设备错误

系统可能因为 USB 设备而无法挂起。您可能会看到以下错误:

PM: Some devices failed to suspend, or early wake event detected
...
xhci_hcd 0000:02:00.0: PM: failed to suspend async: error -16

lspci 可能会为您提供有关故障设备的更多信息。

$ lspci -s 02:00.0
02:00.0 USB controller: Advanced Micro Devices, Inc. [AMD] 500 Series Chipset USB 3.1 XHCI Controller

尝试断开该端口上的设备连接。

网络唤醒 (Wake-on-LAN)

如果启用了 网络唤醒 (Wake-on-LAN),则即使计算机处于休眠状态,网卡也会消耗电力。

挂起后立即唤醒

请参阅 Wakeup triggers#Instantaneous wakeup after suspending

休眠时系统未关机

当您休眠系统时,系统应关机(在将状态保存到磁盘后)。在某些固件上,S4 睡眠状态不可靠。例如,系统可能不会关机,而是重启或保持开机但无响应。如果发生这种情况,请尝试在 sleep.conf.d(5) 中将 HibernateMode 设置为 shutdown,这可能会有所帮助。

/etc/systemd/sleep.conf.d/hibernatemode.conf
[Sleep]
HibernateMode=shutdown

使用上述配置,如果其他一切设置正确,调用 systemctl hibernate 时,机器将关机,同时将状态保存到磁盘。

休眠后启动时未找到操作系统(或启动了错误的操作系统)

当启动磁盘是外部磁盘时,可能会发生这种情况,这似乎是由 BIOS/固件限制引起的。BIOS/固件尝试从内部磁盘启动,而休眠是从外部(或其他)磁盘上的操作系统完成的。

#System does not power off when hibernating 中所示,将 HibernateMode=shutdown 设置为永久解决问题。如果您已经将自己锁定在外面,可以尝试重启系统 4 次(每次等待错误出现),这会在某些 BIOS 上强制执行正常启动过程。

Swap 文件在 /home

如果 swap 文件位于 /home/,则 systemd-logind 将无法访问它,这将导致 Call to Hibernate failed: No such file or directory 警告消息,并在 systemctl hibernate 时需要进行身份验证。应避免这种设置,因为它被认为是 上游不支持 的。请参阅 systemd issue 15354 上的两个解决方法。

A520I 和 B550I 主板无法从睡眠中唤醒

在某些带有 A520i 和 B550i 芯片组的主板上,系统无法完全进入或退出睡眠状态。症状包括系统进入睡眠状态并关闭显示器,而主板上的内部 LED 或电源 LED 保持亮起。随后,系统将无法从该状态恢复,需要强制关机。如果您遇到类似的 AMD 问题,请首先确保您的系统已完全更新,并检查是否安装了 AMD 微码软件包。

验证以 GPP0 开头的行是否处于启用状态。

$ cat /proc/acpi/wakeup
Device	S-state	  Status   Sysfs node
GP12	  S4	*enabled   pci:0000:00:07.1
GP13	  S4	*enabled   pci:0000:00:08.1
XHC0	  S4	*enabled   pci:0000:0b:00.3
GP30	  S4	*disabled
GP31	  S4	*disabled
PS2K	  S3	*disabled
GPP0	  S4	*enabled   pci:0000:00:01.1
GPP8	  S4	*enabled   pci:0000:00:03.1
PTXH	  S4	*enabled   pci:0000:05:00.0
PT20	  S4	*disabled
PT24	  S4	*disabled
PT26	  S4	*disabled
PT27	  S4	*disabled
PT28	  S4	*enabled   pci:0000:06:08.0
PT29	  S4	*enabled   pci:0000:06:09.0

如果已启用,您可以运行以下命令将其禁用:

# echo GPP0 > /proc/acpi/wakeup

现在通过运行 systemctl suspend 来测试,让系统进入睡眠状态。然后尝试在几秒钟后唤醒系统。如果有效,您可以使该解决方法永久化。 创建一个 systemd 单元文件

/etc/systemd/system/toggle-gpp0-to-fix-wakeup.service
[Unit]
Description="Disable GPP0 to fix suspend issue"

[Service]
ExecStart=/bin/sh -c "/bin/echo GPP0 > /proc/acpi/wakeup"

[Install]
WantedBy=multi-user.target

运行 daemon-reload启动/启用新创建的单元。

或者,您可以创建一个 udev 规则。假设 GPP0 的 sysfs 节点是 pci:0000:00:01.1,如下例所示,运行 udevadm info -a -p /sys/bus/pci/devices/0000\:00\:01.1 获取相关信息并创建一个如下的 udev 规则:

/etc/udev/rules.d/10-gpp0-acpi-fix.rules
KERNEL=="0000:00:01.1", SUBSYSTEM=="pci", DRIVERS=="pcieport", ATTR{vendor}=="0x1022", ATTR{device}=="0x1483", ATTR{power/wakeup}="disabled"

udev 守护进程默认情况下已在监视系统更改。如果需要,您可以 手动重新加载规则

笔记本电脑 Fn 键挂起不起作用

如果无论 logind.conf 中的设置如何,睡眠按钮都不起作用(按下它甚至不会在 syslog 中产生消息),那么 logind 可能没有监视键盘设备。[11] 请执行以下操作:

# journalctl --grep="Watching system buttons"

您可能会看到类似以下内容:

May 25 21:28:19 vmarch.lan systemd-logind[210]: Watching system buttons on /dev/input/event2 (Power Button)
May 25 21:28:19 vmarch.lan systemd-logind[210]: Watching system buttons on /dev/input/event3 (Sleep Button)
May 25 21:28:19 vmarch.lan systemd-logind[210]: Watching system buttons on /dev/input/event4 (Video Bus)

注意没有键盘设备。请按如下方式列出键盘设备:

$ stat -c%N /dev/input/by-id/*-kbd
...
/dev/input/by-id/usb-SIGMACHIP_USB_Keyboard-event-kbd -> ../event6
...

现在获取父键盘设备的 ATTRS{name} [12]。例如,在上面的列表中,这个键盘设备有 event6 作为设备输入事件,可以用来搜索其相应的属性名称:

# udevadm info -a /dev/input/event6
...
KERNEL=="event6"
...
ATTRS{name}=="SIGMACHIP USB Keyboard"

现在编写一个自定义 udev 规则来添加“power-switch”标签:

/etc/udev/rules.d/70-power-switch-my.rules
ACTION=="remove", GOTO="power_switch_my_end"
SUBSYSTEM=="input", KERNEL=="event*", ATTRS{name}=="SIGMACHIP USB Keyboard", TAG+="power-switch"
LABEL="power_switch_my_end"

重新加载 udev 规则重启 systemd-logind.service 后,您应该在 logind 的日志中看到 Watching system buttons on /dev/input/event6

系统冻结 60 秒后唤醒,或唤醒后挂起

自 systemd v256 起,systemd 在挂起前会冻结 user.slice。此过程可能由于内核错误而失败,特别是在使用 KVM 时。[13][14]

日志中的消息将在挂起前包含 Failed to freeze unit 'user.slice'。发生此类问题时,尝试登录(启动另一个会话)会失败,并出现以下错误:

pam_systemd(process:session): Failed to create session: Job 9876 for unit 'session-6.scope' failed with 'frozen'

要临时恢复到旧行为,请 编辑 systemd-suspend.servicesystemd-hibernate.servicesystemd-hybrid-sleep.servicesystemd-suspend-then-hibernate.service,并添加以下 drop-in:

[Service]
Environment=SYSTEMD_SLEEP_FREEZE_USER_SESSIONS=false

然而,这个 drop-in 本身也可能阻止系统进入睡眠状态。[15]

休眠和多系统引导

如果您运行的是多重启动系统(包括但不限于 与 Windows 双启动)并希望在主 Arch Linux 处于休眠状态时能够启动到其他系统,您必须格外小心,不要挂载仍在使用中的文件系统。在尝试在另一个系统中挂载此类文件系统之前,您必须确保在休眠系统之前已卸载该文件系统。这可以通过 sleep hooks 实现。

此问题尤其与 EFI 系统分区 相关,因为 ESP 预计会被多个系统共享。请参阅 EFI system partition 中对应的章节以获取缓解策略,这些策略也适用于其他文件系统。