跳转到内容
来自 Arch Linux 中文维基

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

Suspend to idle(挂起到空闲状态)
英特尔称之为 S0ix,微软称之为现代待机(以前是“保持网络连接的待机”),内核称之为 S2Idle。设计用于替代 S3 睡眠状态的支持系统,提供相同的节能,但大大减少了唤醒时间。
提示:虽然这种状态会导致 WindowsmacOS 上的电池消耗问题,因为它们支持在这种状态下唤醒设备进行网络活动,但 Linux 软件生态系统目前没有使用这种特性,应该不会受到影响。
Suspend to RAM(挂起到内存,通称挂起)
ACPI 定义的 S3 睡眠状态。通过将机器中大多数和 RAM 不相关的部件断电来工作,RAM 是恢复机器状态所必需的。由于可以节省大量电力,建议笔记本电脑在使用电池运行且盖子关闭时(或用户在一段时间内处于非活动状态)自动进入此模式。
Suspend to disk(挂起到硬盘,通称休眠)
ACPI 定义的 S4 睡眠状态。将机器状态保存到交换空间并关闭机器。再次开机后,恢复状态。直到开机前,机器都不会有任何待机功耗
Hybrid suspend(混合挂起)
挂起和休眠的混合,有时称为挂起到两者。将机器状态保存到交换空间,但并不关闭电脑,而是调用默认的挂起机制,从而使未掉电的电脑能立刻恢复。如果电脑掉电(断电且电池耗尽),系统也可以从硬盘的交换空间中恢复,尽管比从内存中恢复慢一些。

以上三者均由内核提供基础功能,再由高级接口进行了一定调整,以适配一些较为复杂的硬件与内核模块(如显卡唤醒后的重新初始化)


内核接口 (swsusp)

注意:尽管可以直接使用内核接口,这更快,因为不需要额外的用户空间处理,但建议使用高级接口。它通过执行额外的安全检查以及提供预挂起和后挂起钩子机制来更好地设置硬件时钟、恢复无线等,从而做得更好。

可以通过直接告知内核中的软件挂起代码(swsusp)进入挂起状态,具体的方法和状态取决于硬件支持的程度。在现代内核中,向 /sys/power/state 写入特定字符串是触发此挂起的主要机制。

更多信息详见内核文档

高级接口 (systemd)

注意:高级接口旨在提供可执行的二进制文件/脚本,用于执行挂起/休眠操作,并提供将额外的准备/清理工作挂钩到所述进程的方法。有关电源按钮、菜单点击或笔记本盖事件自动进入睡眠状态,请参阅 Power management#ACPI events

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

systemctl suspend 开箱即用。要使 systemctl hibernate 在你的系统上运行,你可能需要遵循 #休眠中的说明。

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

  • systemctl hybrid-sleep 将系统同时挂起到 RAM 和磁盘,因此完全断电不会导致数据丢失。 此模式也称为“同时挂起”。
  • 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,如果不支持则回退到 suspendhibernate。更多信息请参见 systemctl(1)

关于配置睡眠或休眠钩子,详见电源管理#睡眠钩子。又见 systemctl(1)systemd-sleep(8)systemd.special(7)

更改挂起方法

在 S0ix 挂起不能提供与常规 S3 睡眠相同的节能的系统上,或者当节能优于快速的恢复时间时,可以更改默认挂起方法。

提示:S0ix 被认为可以提供与 S3 睡眠相同或更好的节能,请参阅以下英特尔博客文章 如何在 Linux 中实现 S0ix 状态Linux S0ix 故障排除在 Linux 上高效空闲:一个案例研究,以检查你是否可以使其按预期工作。英特尔用户可以使用 S0ixSelftestTool。AMD 用户可以使用 amd-debug-tools

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

$ cat /sys/power/mem_sleep
[s2idle] shallow deep
mem_sleep 中支持的睡眠状态
mem_sleep 字符串 睡眠状态
s2idle suspend-to-idle
shallow standby
deep suspend-to-RAM

如果你的硬件没有公布 deep 睡眠状态,请首先检查 UEFI 是否公布了它的某些设置,通常是在“电源(Power)”或“睡眠状态(Sleep state)”或类似的用词下,并使用名为 Windows 10, Windows and LinuxS3/Modern standby support 关于 S0ix 的选项,以及 Legacy, Linux, Linux S3S3 enabled 关于 S3 睡眠的选项。否则,你可以继续使用 s2idle,考虑使用休眠或尝试给 DSDT 表打补丁(或在线查找补丁版本)。

注意:最后一个解决方案很可能会引起问题。自从 Windows 系统默认使用 “现代待机 ”后,制造商已经停止修复 ACPI S3 状态的错误:如果它们主动地不公布它,它可能会在某种程度上出问题。

通过在更改睡眠方法后测试几个睡眠周期,验证你的硬件没有 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

休眠

为了使用休眠功能,你必须创建一个 swap 分区或文件、#配置 initramfs 以便在早期用户空间启动恢复过程,并指定交换空间的位置以便 initramfs 可以访问,例如:由 systemd 定义的 HibernateLocation EFI 变量或 resume= 内核参数。下面详细描述了这三个步骤。

注意:

swap分区/文件的大小

即使交换分区比内存小,成功休眠的可能性仍然不小。参见内核文档中的“image_size”部分以获取关于 image_size sysfs(5) 伪文件的更多信息。

你可以减小 /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

  • 当使用一个 busybox-based 的 initramfs 时(这是默认值),/etc/mkinitcpio.conf 中需要 resume 钩子。无论是通过卷标还是通过 UUID,交换分区都是通过 udev 设备节点来引用的,因此 resume 钩子必须在 udev 钩子之后。这是一个使用默认钩子配置的示例:
HOOKS=(base udev autodetect microcode modconf kms keyboard keymap consolefont block filesystems resume fsck)
编辑后记得重新生成 initramfs
注意:如果使用堆叠存储作为交换空间,例如 dm-cryptRAIDLVM,则最终映射的设备必须在早期用户空间中可用,并且在恢复过程启动之前。也就是说,在这样的设置中,resume 钩子必须放在诸如 encryptlvm2 等钩子之后。
  • 当使用带有 systemd 钩子的 initramfs 时,已经提供了恢复机制,不需要额外添加钩子。

传递休眠位置到 initramfs

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

systemd v255 和 mkinitcpio v38 开始,当系统运行在 UEFI 上时,systemd-sleep(8) 将自动选择合适的交换空间进行休眠,并且所用交换空间的信息存储在 HibernateLocation EFI 变量中。在下次启动时,systemd-hibernate-resume(8) 从 EFI 变量读取位置,系统恢复。这意味着除非系统使用遗留 BIOS 或你想选择与自动选择的不同的交换空间,否则以下步骤不是必需的。

手动指定休眠位置

可以传递内核参数 resume=swap_device。其中 swap_device 遵循 persistent block device naming。例如:

  • resume=UUID=4209c845-f495-4c43-8a03-5363dd433153
  • resume="PARTLABEL=swap分区卷标"
  • resume=/dev/archVolumeGroup/archLogicalVolume——如果swap在LVM逻辑卷上(UUID 和 标签应该同样可以工作)

内核参数只有在重新启动后才会生效。要想不重启而马上休眠,请从lsblk获取卷的主要和次要设备号,并 echo major:minor/sys/power/resume

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

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

如果使用交换文件,请继续查看#获取交换文件偏移量

获取交换文件偏移量

使用交换文件进行休眠时,文件系统所在的块设备应该在 resume= 中指定,并且还需要通过 resume_offset= 内核参数指定交换文件的物理偏移量。 [3]

在除 Btrfs 之外的文件系统上,可以通过运行 filefrag -v 交换文件 来获取 resume_offset= 的值。输出为表格格式,所需的值位于第一行 physical_offset 列中。

例如:

# filefrag -v /swapfile
Filesystem type is: ef53
File size of /swapfile 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
提示:这个命令也许可以用于查找交换文件所在设备:findmnt -no UUID -T swap_file
注意:对于一个堆叠块设备,比如一个加密容器(LUKS),RAID 或者 LVM,resume 参数必须指向交换文件所在的已解锁或映射的设备。

更改休眠镜像的压缩算法

从 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 仅内存交换时的休眠问题。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%的Data%

警告:不要在用于休眠的精简配置的swap分区上使用TRIM,即:不要在/etc/fstab使用discard,也不要在swapon时加入-d--discard参数。否则已被使用的空间会被重新去分配

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

在 Linux 6.8 中,zswap 增加了 一个按 cgroup 禁用写回的选项。通过在所有可能的单元类型中使用 systemd 单元设置 MemoryZSwapWriteback(参见 systemd.resource-control(5) § Memory Accounting and Control),可以有效地完全禁用 zswap 写回。这使得 zswap 可以像 zram 一样使用,同时还支持休眠。

为了避免手动创建十二个顶级按类型的 drop-in 文件(用于系统和用户的 scopeserviceslicesocketmountswap 单元类型),请 安装 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.targetStopWhenUnneeded=yes,所以钩子保证会为不同的任务正确启动/停止。

位于 /usr/lib/systemd/system-sleep 的钩子

注意:根据 systemd-sleep(8),此方法被视为一种 hack。systemd-sleep 会并行运行这些钩子,而不是一个接一个地运行。有关支持排序的更明确接口,请参见#Custom systemd units

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

  1. 要么是 pre,要么是 post,取决于机器是进入睡眠还是唤醒。
  2. suspendhibernatehybrid-sleepsuspend-then-hibernate,取决于正在调用的是哪个。

任何自定义脚本的输出都将由 systemd-suspend.servicesystemd-hibernate.servicesystemd-hybrid-sleep.service 记录。你可以在 systemd's journalctl 中查看其输出:

# 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

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

一个名为 SYSTEMD_SLEEP_ACTION 的环境变量将被设置,并包含正在处理的睡眠操作。这对于 suspend-then-hibernate 特别有用,其中变量的值将是 suspendhibernatesuspend-after-failed-hibernate(在休眠失败的情况下)。

提示与技巧

在受信任的位置自动解锁

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

/etc/local-scripts/resume-unlock.sh
#!/usr/bin/bash
# 如果在受信任的位置,则解锁会话

function trusted() {
    # 检查是否连接到受信任的 Wi-Fi 网络
    [[ $(iwgetid -r) == your_home_ssid ]] \
        && return 0

    # 检查是否连接了受信任的 USB 设备。
    #lsusb -d xxxx:xxxx && return 0

    return 1 # 不受信任
}

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 Rapid Start Technology, IRST)

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

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

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

警告:英特尔快速启动分区未加密;“如果你使用基于软件的磁盘加密,英特尔建议禁用英特尔快速启动技术”。[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 以获取更多详细信息。

另请参阅英特尔快速启动技术的一般问答用户手册

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

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

故障排除

ACPI_OS_NAME

你也许需要调整你的 DSDT table 来让它工作。参阅 DSDT 词条。

睡眠/休眠无法进行,或无法稳定进行

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

原因: 本节混合了几个无关的原因。(在 Talk:电源管理/挂起与休眠 中讨论)


有相当多的错误报告指出,他们的屏幕在给出可读的错误信息前就关闭了,有时屏幕关闭了,却再也无法唤醒。这些故障在笔记本电脑和台式机上都曾发生。这不是个官方解决方案,但切换到旧的内核,尤其是LTS内核,或许可以修复这个问题。

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

有时屏幕会变黑,这是由于从initramfs内部初始化设备引起的。移除Mkinitcpio#MODULES中可能包含的任何模块,移除kms钩子并重新生成initramfs可能会解决这个问题,特别是对于早期KMS的图形驱动程序。在恢复之前初始化这些设备可能会导致系统无法从休眠中恢复的不一致情况。这不影响从RAM中恢复。此外,请查看博客文章调试挂起问题的最佳实践

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

对于NVIDIA用户,禁用模块nvidiafb可能会有所帮助。[8]

带有Intel CPU的笔记本电脑如果加载了intel_lpss_pci模块用于触摸板,可能会在恢复时遇到内核恐慌(大写锁定键闪烁)[9]。需要将该模块添加到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

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

网络唤醒(WoL, Wake-on-LAN)

如果网络唤醒被启用,网卡可能会消耗电力,即使电脑处于休眠状态。

挂起后被立即唤醒

参见电源管理/唤醒触发器#挂起后被立即唤醒

系统在休眠后没有断电

当你休眠系统时,系统应该会断电(在将状态保存到磁盘后)。在某些固件上,S4 睡眠状态可能无法可靠工作。例如,系统可能会重启或保持开启但无响应,而不是断电。如果发生这种情况,可以在 sleep.conf.d(5) 中将 HibernateMode 设置为 shutdown

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

使用上述配置,如果其他设置都正确,在调用 systemctl hibernate 时,机器将会关闭,并在关闭过程中将状态保存到磁盘。

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

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

类似于#系统在休眠后没有断电,设置 HibernateMode=shutdown 以永久解决问题。如果你已经将自己锁定在外,你可以尝试重新启动系统4次(每次等待错误出现),这在某些BIOS上强制执行正常的引导过程。

Swap file in /home

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

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

在某些使用 A520i 和 B550i 芯片组的主板上,系统无法完全进入睡眠状态或从睡眠状态中恢复。症状包括系统进入睡眠状态,显示器关闭,但主板上的内部 LED 或电源 LED 仍然亮着。随后,系统无法从该状态恢复,需要强制关机。如果你在使用 AMD 时遇到类似问题,请首先确保你的系统已完全更新,并检查是否安装了 AMD microcode 软件包。

验证以 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="禁用 GPP0 以修复睡眠问题"

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

[Install]
WantedBy=multi-user.target

执行 daemon-reloadstart/enable 新创建的单元。

或者,你可以创建一个 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 守护程序已经在监视系统中的更改。如果需要,你可以 手动重新加载规则

Suspend from corresponding laptop Fn key not working

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

# 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} [11]。例如,在上面的列表中,此键盘设备的设备输入事件为 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

System freezes for 60 seconds and then wakes back up or hangs after waking up

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

日志中的消息会在休眠前包含 Failed to freeze unit 'user.slice'。当发生此类问题时,尝试登录(启动另一个会话)将失败,并显示:

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

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

[Service]
Environment=SYSTEMD_SLEEP_FREEZE_USER_SESSIONS=false