电源管理/挂起与休眠
有多种方法可用于挂起,特别是:
- Suspend to idle(挂起到空闲状态)
- 英特尔称之为 S0ix,微软称之为现代待机(以前是“保持网络连接的待机”),内核称之为 S2Idle。设计用于替代 S3 睡眠状态的支持系统,提供相同的节能,但大大减少了唤醒时间。
- Suspend to RAM(挂起到内存,通称挂起/睡眠)
- ACPI 定义的 S3 睡眠状态。通过将机器中大多数和 RAM 不相关的部件断电来工作,RAM 是恢复机器状态所必需的。由于可以节省大量电力,建议笔记本电脑在使用电池运行且盖子关闭时(或用户在一段时间内处于非活动状态)自动进入此模式。
- Suspend to disk(挂起到硬盘,通称休眠)
- ACPI 定义的 S4 睡眠状态。将机器状态保存到交换空间并关闭机器。再次开机后,恢复状态。直到开机前,机器都不会有任何待机功耗。
- Suspend to both(挂起到两者)
- 挂起和休眠的混合,有时称为混合挂起。将机器状态保存到交换空间,但并不关闭电脑,而是调用默认的挂起机制,从而使未掉电的电脑能立刻恢复。如果电脑掉电(断电且电池耗尽),系统也可以从硬盘的交换空间中恢复,尽管比从内存中恢复慢一些。
以上三者均由许多底层接口提供基础功能,再由高级接口进行一定调整,以适配一些较为复杂的硬件与内核模块(如显卡唤醒后的重新初始化)
底层接口[编辑 | 编辑源代码]
直接使用低级接口显然会比任何高级接口都快很多,因为执行睡眠/休眠前后的钩子(hooks)需要时间。然而,钩子可以提供很多功能,如正确设置设备时钟、恢复无线网络状态等。因此,虽然这些接口可以被直接使用,我们仍然建议用户使用高级接口进行睡眠/休眠。
内核休眠(软件休眠,swsusp)[编辑 | 编辑源代码]
最直接的方法是直接告知内核中的软件挂起代码(swsusp)进入挂起状态,具体的方法和状态取决于硬件支持的程度。在现代内核中,向 /sys/power/state
写入特定字符串是触发此挂起的主要机制。
更多信息详见内核文档。
用户空间软件休眠(uswswsp)[编辑 | 编辑源代码]
uswsusp(“Userspace Software Suspend”)是内核Suspend to RAM机制的封装,该机制在挂起前和恢复后从用户空间执行一些有关图形适配器的操作。
详见 Uswsusp。
高级接口[编辑 | 编辑源代码]
这些软件包的目标是提供二进制文件或脚本,使用户可以调它们来进行睡眠或休眠。在实际使用中,往往由其它软件(如桌面环境/DE)将它们与电源按钮、菜单点击或笔记本电脑盖事件联系起来。如果不想借助其他软件调用高级接口,又希望设备能在某些电源事件(如合上盖子或电池耗尽)时自动睡眠/休眠,你也许需要看看Acpid。
systemd[编辑 | 编辑源代码]
systemd为睡眠、休眠和混合休眠提供原生支持。详见电源管理#用 systemd 进行电源管理。systemd 是 Arch Linux 默认使用的接口。
关于配置睡眠或休眠钩子,详见电源管理#睡眠钩子。又见 systemctl(1)、systemd-sleep(8) 和 systemd.special(7)。
更改挂起方法[编辑 | 编辑源代码]
在 S0ix 挂起不能提供与常规 S3 睡眠相同的节能的系统上,或者当节能优于快速的恢复时间时,可以更改默认挂起方法。
首先,检查硬件是否公布支持其他方法,如下面的示例所示:
$ cat /sys/power/mem_sleep
[s2idle] shallow deep
如果您的硬件没有公布 deep
睡眠状态,请首先检查 UEFI 是否公布了它的某些设置,通常是在“电源(Power)”或“睡眠状态(Sleep state)”或类似的用词下,并使用名为 Windows 10, Windows and Linux 或 S3/Modern standby support 关于 S0ix 的选项,以及 Legacy, Linux, Linux S3 或 S3 enabled 关于 S3 睡眠的选项。否则,您可以继续使用 s2idle
,考虑使用休眠或尝试给 DSDT 表打补丁(或在线查找补丁版本)。
通过在更改睡眠方法后测试几个睡眠周期,测试硬件是否没有 S3 睡眠问题:
# echo "deep" > /sys/power/mem_sleep
如果没有发现任何问题,可以使用 mem_sleep_default=deep
内核参数使更改永久化。
在一些相反的情况下,有缺陷的固件公布支持 deep
睡眠,但只支持 s2idle
。在这种情况下,通过 sleep.conf.d(5) 中的 SuspendState
可以以另一种方法使用 s2idle
:
/etc/systemd/sleep.conf.d/freeze.conf
[Sleep] SuspendState=freeze
休眠[编辑 | 编辑源代码]
为了使用休眠,用户需要创建一个交换分区或文件。使用resume= 内核参数向内核传递swap信息,该参数应通过引导程序配置。您还需要#配置 initramfs。这将让内核尝试从早期用户空间中的指定swap分区/文件恢复。下面将详细描述这三个步骤。
swap分区/文件的大小[编辑 | 编辑源代码]
即使swap比内存小,成功休眠的可能性仍然很大。参见内核文档中的“image_size”部分以获取关于 image_size
sysfs(5) pseudo-file 的更多信息。
您可以减小 /sys/power/image_size
的设定值以使休眠镜像尽可能小(对于小交换分区),或者增大它以加快休眠过程(也许)。对于 RAM 很大的系统,较小的值可能会显著提高系统从休眠中恢复的速度。这是一个临时文件,参阅Systemd#临时文件以固定你的修改。
/etc/tmpfiles.d/hibernation_image_size.conf
# Path Mode UID GID Age Argument w /sys/power/image_size - - - - 0
休眠镜像不能跨多个交换分区和/或交换文件。它必须完全适配一个交换分区或一个交换文件[1]
配置 initramfs[编辑 | 编辑源代码]
- 当使用带有
base
钩子的 initramfs 时(这是默认值),/etc/mkinitcpio.conf
中需要resume
钩子。无论是通过卷标还是通过 UUID,交换分区都是通过 udev 设备节点来引用的,因此resume
钩子必须在udev
钩子之后。这是一个使用默认钩子配置的示例:
HOOKS=(base udev autodetect keyboard modconf block filesystems resume fsck)
- 编辑后记得重新生成 initramfs。
- 当使用带有
systemd
钩子的 initramfs 时,已经提供了恢复机制,不需要额外添加钩子。
必需的内核参数[编辑 | 编辑源代码]
想要休眠,必须传递内核参数 resume=swap_device
。(以 persistent block device naming 方法中的任何一个替换掉swap_device
)如:
resume=UUID=4209c845-f495-4c43-8a03-5363dd433153
resume="PARTLABEL=swap分区卷标"
resume=/dev/archVolumeGroup/archLogicalVolume
——如果swap在LVM逻辑卷上(UUID 和 标签应该同样可以工作)
内核参数只有在重新启动后才会生效。要想不重启而立即休眠,请从lsblk获取卷的主要和次要设备号,并 echo major:minor
到/sys/power/resume
。如果使用交换文件,则还应将 resume offset 添加到/sys/power/resume_offset
。 [2]
例如,如果交换设备是8:3
:
# echo 8:3 > /sys/power/resume
或者在休眠到交换文件时,如果交换文件位于卷8:2
上并且offset为38912
:
# echo 8:2 > /sys/power/resume # echo 38912 > /sys/power/resume_offset
休眠到交换文件[编辑 | 编辑源代码]
使用交换文件需要设置 resume=UUID=swap_device_uuid
,另外还需要设置resume_offset=swap_file_offset
内核参数。见内核文档。
swap_device
是交换文件所在的卷,其格式与root parameter相同。swap_file_offset
的值可以通过运行filefrag -v swap_file
来获得,输出为表格形式,所需的值位于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 ...
在本例中,swap_file_offset
的值是38912
(第一行physical_offset下面,两个英文句点..
前面的数字)。
- 以下命令的结果可用于获取
swap_device_uuid
:findmnt -no UUID -T /swapfile
- 以下命令的结果可用于获取
swap_file_offset
:filefrag -v /swapfile | awk '$1=="0:" {print substr($4, 1, length($4)-2)}'
swap_file_offset
的值也可以通过运行swap-offset swap_file
来获得。工具集 Uswsusp 中提供了 swap-offset 二进制文件。如果使用此方法,则必须在/etc/suspend.conf
通过键resume device
和resume offset
。在这种情况下,不需要重新启动。
- 对于堆叠块设备,如加密容器(LUKS)、RAID或LVM,
resume
参数必须指向包含交换文件的文件系统的未锁定/映射设备。 - 如果交换文件在
/home/
中,则systemd-logind将无法确定其大小,因此将阻止休眠,并显示以下消息:Failed to hibernate system via logind: Not enough swap space for hibernation
。解决办法见systemd issue 15354。
休眠到 Btrfs 上的交换文件中[编辑 | 编辑源代码]
不要尝试使用 filefrag 工具,在 Btrfs 上,从 filefrag 获得的 physical_offset 不是实际 offset。因为 btrfs 有一个虚拟磁盘地址空间以支持多个设备。[3]
自 btrfs-progs包 6.1,提供了一个命令来检查用于休眠的交换文件及其偏移值。参见 btrfs-inspect-internal(8)。
# btrfs inspect-internal map-swapfile -r path/to/swapfile
198122980
休眠到精简配置的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%
/etc/fstab
使用discard
,也不要在swapon时加入-d
或--discard
参数。否则已被使用的空间会被重新去分配需要添加resume=/dev/sdxY (sdxY 是swap分区的名字) ,让系统在启动时读取swap分区中的内容。
英特尔快速启动技术(Intel Rapid Start Technology,IRST)[编辑 | 编辑源代码]
英特尔快速启动技术是一种休眠的固件解决方案,允许在预定义的时间间隔或电池状态后从休眠。这应该比常规休眠更快更可靠,因为它是由固件而不是在操作系统级别完成的。通常,它必须在固件中启用,固件还支持设置暂停/电池事件触发休眠后的持续时间,然而,尽管固件中支持 IRST,但某些设备仅允许通过英特尔的 Windows 驱动程序进行配置。在这种情况下,下面描述的 intel-rst 内核模块应该能够在 Linux 下配置事件。
启用英特尔快速启动技术(IRST)后,从深度睡眠中恢复需要“比从 S3 恢复长几秒,但比从休眠中恢复快得多”。
许多基于英特尔的系统都支持 IRST 固件,但需要在 SSD(而不是 HDD)上使用特殊分区。Windows 的 OEM 部署可能已经有一个预先存在的 IRST 分区,可以在 Arch Linux 安装过程中保留(而不是擦除和重新分区整个 SSD)。它应该显示为与系统 RAM 大小相等的未格式化分区。
但是,如果您打算擦除并重新分区整个驱动器(或者已经这样做了),那么如果您也计划使用该技术,则必须重新创建IRST分区。这可以通过创建一个与系统 RAM 大小相等的空分区,并将其分区类型设置为 GUID D3BFE2DE-3DAF-11DF-BA40-E3A556D89593
(对于 GPT 分区)或 ID 0x84
(对于 MBR 分区)来实现。您可能还需要在系统的固件设置中启用对IRST的支持。
IRST 休眠过程的持续时间(即将RAM的全部内容复制到特殊分区)取决于系统的 RAM 大小和 SSD 速度,因此可能需要20–60秒。一些系统可以用 LED 指示灯指示过程的完成,例如停止闪烁。
在 Linux 中启用 IRST 休眠事件需要配置 CONFIG_INTEL_RST=y
,如果要从模块载入,则需要加载 intel-rst 模块。一旦通过 modprobe intel_rst
加载,它应该创建了 wakeup_events 和 wakeup_time 文件,例如/sys/bus/acpi/drivers/intel_rapid_start/*/wakeup_events
,这些文件可用于进一步配置。该模块有简单的文档,请参见源文件 drivers/platform/x86/intel/rst.c 了解更多详情。
故障排除[编辑 | 编辑源代码]
ACPI_OS_NAME[编辑 | 编辑源代码]
你也许需要调整你的 DSDT table 来让它工作。 参阅 DSDT 词条。
睡眠/休眠无法进行,或无法稳定进行[编辑 | 编辑源代码]
有相当多的错误报告指出,他们的屏幕在给出可读的错误信息前就关闭了,有时屏幕关闭了,却再也无法唤醒。这些故障在笔记本电脑和台式机上都曾发生。(这不是个官方解决方案),但切换到旧的内核,尤其是LTS内核,或许可以修复这个问题。
使用硬件看门狗(默认是禁用的,详阅systemd-system.conf(5) § OPTIONS中的RuntimeWatchdogSec=
条目。存在bug的硬件看门狗可能会在系统成功创建休眠镜像前就重置电脑状态。
有时屏幕会因为 initramfs 中进行的设备初始化过程而变黑。移除 Mkinitcpio#模块(MODULES)里你可能设置了的任何模块,尤其是KMS显卡驱动 KMS_早启动并重新构建initramfs,也许能解决这个问题。唤醒前初始化这样的设备可能会带来一些冲突,这些冲突将阻止系统从休眠中唤醒。但这一般不会影响系统从睡眠(挂起)中唤醒。可参考一篇博文: best practices to debug suspend issues。
将旧的 radeon 驱动换成新的 AMDGPU 驱动也许能让休眠与唤醒过程更加成功。
对于英特尔核芯显卡,启用 early KMS 也许能解决休眠黑屏的问题。详见 KMS_早启动。
在升级到内核版本 4.15.3 后,唤醒系统时可能会黑屏,只在黑屏上留下一个静止不动的鼠标指针。将模块 nvidiafb
加入黑名单可能会有帮助。[5]
使用 Intel CPU 并且为触摸板加载 intel_lpss_pci
模块的笔记本电脑,可能会在唤醒时发生内核崩溃(Caps Lock灯闪烁)。[6]这个模块需要以这样的方式加入 initramfs 里:
/etc/mkinitcpio.conf
MODULES=(... intel_lpss_pci ...)
网络唤醒(WoL, Wake-on-LAN)[编辑 | 编辑源代码]
如果网络唤醒被启用,网卡可能会消耗电力,即使电脑处于休眠状态。
挂起后被立即唤醒[编辑 | 编辑源代码]
如果您在 AMD CPU 上使用 Linux 内核6.1及更高版本,这也可能是由内核中s3相关的控制策略问题引起的。临时解决方案是关闭相关 i2c 设备的唤醒。要找到它可以通过
$ ls /sys/bus/i2c/devices/*/power/wakeup
设备名称的格式应为 i2c-ELAN0679:00
或 i2c-MSFT0001:00
。然后,测试以下命令是否可以进入挂起:
# echo disabled > /sys/bus/i2c/devices/device_name/power/wakeup # systemctl suspend
如果它有效,您可以通过添加 udev 规则来持久化此配置:
/etc/udev/rules.d/99-avoid-i2c-wakeup.rules
KERNEL=="device_name", SUBSYSTEM=="i2c", ATTR{power/wakeup}="disabled"
系统在休眠后没有断电[编辑 | 编辑源代码]
当你把你的系统休眠,系统应该(在保存状态后)断电。有时你会看见电源 LED 仍然亮着。如果存在这样的情况,在 sleep.conf.d(5) 里把 HibernateMode
设置为 shutdown
也许有用:
/etc/systemd/sleep.conf.d/hibernatemode.conf
[Sleep] HibernateMode=shutdown
在进行了上述设置后,如果其它一切正常,当调用 systemctl hibernate
时,电脑就会正常地休眠后断电了。
休眠后启动时未找到操作系统(或启动错误的操作系统)[编辑 | 编辑源代码]
当引导磁盘是外部磁盘时,可能会发生这种情况,这似乎是由 BIOS/固件限制引起的。BIOS/固件尝试从内部磁盘启动,而休眠是从外部(或其他)磁盘上的操作系统完成的。
类似于#系统在休眠后没有断电,设置 HibernateMode=shutdown
以永久解决问题。如果您已经将自己锁定在外,您可以尝试重新启动系统4次(每次等待错误出现),这在某些BIOS上强制执行正常的引导过程。