从一个交换文件中休眠和恢复

我在笔记本中配置了一个新的Ubuntu安装,以使用交换文件,而不是使用交换分区。

默认情况下,Ubuntu无法使用交换文件进行休眠,所以我试过这个教程,但它特定于grub1,而Ubuntu现在使用的是grub2

有人知道如何做这个吗?


我不知道systemd怎么回事,看了代码应该原生支持swapfile。然后我用了好几年,一个比RAM小得多的swap分区,再加上一个swapfile来弥补。在最新版本中,我不得不调整优先级才能让它正常工作(降低swap分区的优先级,但是从我记得的情况来看,我可能搞错了,实际上需要使用swap分区)。但与此同时,我帮助了另一个遇到同样问题的人,删除了这个本身就太小的swap分区就导致系统崩溃。一个小的swap分区pri=0,一个大的swap文件pri=1,对我们两个都有效。 - Thomas Guyot-Sionnest
NB:优先级变更解决了恢复问题,挂起一直正常工作,但是在initrd阶段,恢复操作无法获取交换文件中的映像。我假设工作引导使用分区来作为恢复映像... 在我修复systemd之前,swsusp s2disk(以及可能还有pm-hibernate)都可以正常工作,不需要任何更改。我认为,要么交换文件允许内核进一步减小映像大小(例如,将应用程序内存交换出去),要么systemd一直支持交换文件,但最近的一个错误部分破坏了它。 - Thomas Guyot-Sionnest
6个回答

这是我在Ubuntu 18.04上使其正常工作的方法。
- 确保你的/swapfile大小至少与你的RAM相同。
sudo swapoff /swapfile
sudo dd if=/dev/zero of=/swapfile bs=$(cat /proc/meminfo | awk '/MemTotal/ {print $2}') count=1024 conv=notrunc
sudo mkswap /swapfile
sudo swapon /swapfile

请注意包含您的/swapfile的分区的UUID:
$ sudo findmnt -no UUID -T /swapfile
20562a02-cfa6-42e0-bb9f-5e936ea763d0

重新配置软件包uswsusp以正确使用交换文件:
sudo dpkg-reconfigure -pmedium uswsusp
# Answer "Yes" to continue without swap space
# Select "/dev/disk/by-uuid/20562a02-cfa6-42e0-bb9f-5e936ea763d0" replace the UUID with the result from the previous findmnt command
# Encrypt: "No"

使用sudo systemctl edit systemd-hibernate.service编辑SystemD休眠服务,并填入以下内容:
[Service]
ExecStart=
ExecStartPre=-/bin/run-parts -v -a pre /lib/systemd/system-sleep
ExecStart=/usr/sbin/s2disk
ExecStartPost=-/bin/run-parts -v --reverse -a post /lib/systemd/system-sleep

请注意您的/swapfile的偏移量:
$ sudo swap-offset /swapfile
resume offset = 34818

配置Grub以从交换文件中恢复,方法是编辑/etc/default/grub并修改以下行:
GRUB_CMDLINE_LINUX_DEFAULT="resume=UUID=20562a02-cfa6-42e0-bb9f-5e936ea763d0 resume_offset=34818 quiet splash"

更新Grub:
sudo update-grub

创建以下文件/etc/initramfs-tools/conf.d/resume
RESUME=UUID=20562a02-cfa6-42e0-bb9e-5e936ea763d0 resume_offset=34816
    # Resume from /swapfile

更新initramfs:
sudo update-initramfs -u -k all

现在您可以使用sudo systemctl hibernate进入休眠模式。

您还可以创建这些脚本:

sudo tee /usr/local/bin/gotosleep <<EOF
dbus-send --type=method_call --dest=org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver.Lock
sleep 2
sudo /usr/sbin/s2both
EOF
sudo chmod +x /usr/local/bin/gotosleep
sudo tee /usr/local/bin/gotohibernation <<EOF
dbus-send --type=method_call --dest=org.gnome.ScreenSaver /org/gnome/ScreenSaver org.gnome.ScreenSaver.Lock
sleep 2
sudo systemctl hibernate
EOF
sudo chmod +x /usr/local/bin/gotohibernation

所以你可以使用 gotosleep 进入睡眠状态,或者使用 gotohibernation 进入休眠状态。

为了让上述脚本能够正常工作,你必须能够执行 sudo s2bothsudo s2ramsudo systemctl hibernate 而无需输入密码。

你可以通过创建一个名为 powerdev 的用户组,并将当前用户添加到该用户组中,并配置以下sudoers配置文件(使用 sudo visudo -f /etc/sudoers.d/powerdev 来编辑):

%powerdev ALL=NOPASSWD: /usr/sbin/s2both, /usr/sbin/s2ram, /bin/systemctl hibernate

使用的文档:


2非常感谢您!我尝试了很多方法,但只有您的方法在Ubuntu 18.04上对我有效 :) - jirislav
这几乎让我想再试一次冬眠 - 我的磁盘偶尔出现损坏问题,显然与冬眠过程有关。你遇到过类似的情况吗? - Charles Green
感谢这个指南。按照步骤进行操作后,尝试使用$ sudo systemctl hibernate让我的笔记本进入休眠状态,但是几秒钟后又自动唤醒了。/var/log/syslog显示了许多错误信息,以下是其中最相关的几条:
  • 无法为输出eDP-1设置省电模式:权限被拒绝 ...
  • 对象.Gjs_AppIndicatorIconActor__1(0x561c60a4d150)已经被终结,无法对其设置任何属性。 ...
  • s2disk:无法使用恢复设备(请尝试swapon -a)。原因:设备不存在
  • systemd-hibernate.service:主进程退出,代码=退出,...
- Jelani Nelson
@JelaniNelson 看起来你的交换配置不正确。 - Anthony O.
谢谢,确实你是对的。现在已经全部修复了!我在/etc/uswsusp.conf中不小心设置了错误的偏移量。 - Jelani Nelson
我按照描述的方式进行了操作,但是出现了一个错误。我有一个加密的文件系统,并且有一个9GB的交换文件。s:推迟更新(触发器已激活) 正在处理initramfs-tools(0.136ubuntu6)的触发器... update-initramfs: 正在生成/boot/initrd.img-5.4.0-25-generic W: initramfs-tools配置设置RESUME=UUID=8fb86658-3799-4204-94df-7cf37441baa3 W: 但没有匹配的交换设备可用。//n// sudo findmnt -no SOURCE,UUID -T /swapfile /dev/mapper/sda6_crypt 8fb86658-3799-4204-94df-7cf37441baa3 - Arno
1谢谢你!这是唯一在Ubuntu 20.04上对我有效的解决方案! - junkystu
非常感谢您提供的解决方案!终于找到了唯一有效的解决方法。顺便说一下,记录一下,我正在使用12年前的Dell D620笔记本电脑上的Lubuntu 20.04,并且它也能正常工作。在找到这个解决方案之前,我尝试了很多其他方法,但是没有一个可以从交换文件中恢复。其他解决方案中缺少了"resume_offset"参数。再次感谢您! - Lye Heng Foo
1这也适用于Ubutu 21.04! - Billy Long
swapoff:/swapfile:swapoff 失败:没有这个文件或目录。需要帮忙吗? - Timo
bs=$(cat /proc/meminfo..) 看起来很奇怪,默认值是 512。这里的单位是以百万字节计算的。这是怎么回事呢? - Timo
谢谢,确实在20.04上有效(在Thinkpad T14 Gen2上)。我遇到了一个这里没有描述的问题,所以我要发帖说明一下:在完成以上步骤后,执行命令sudo systemctl hibernate时出现了Failed to hibernate system via logind: Sleep verb not supported的错误。事实证明,cat /sys/power/state必须返回freeze mem disk,但在我的情况下,disk是缺失的。为了解决这个问题,我只需要在BIOS中禁用安全启动即可。完成后,cat /sys/power/state将返回freeze mem disk,而sudo systemctl hibernate将正常工作。 - Dmitry Frank
在我完成所有这些操作之后,sudo systemctl hibernate 的输出如下:通过 logind 无法使系统休眠:休眠所需的交换空间不足。我尝试增加交换空间,但没有成功。现在我有100GB的交换空间和32GB的RAM,但问题依然存在。 - pieroxy
为什么你没有接受任何答案? - vanadium
@vanadium 因为我没有时间测试所有的解决方案以找到最好的一个 ;) - Anthony O.

使用uswusp的交换文件休眠

虽然可以通过设置内核参数在swap文件中进行休眠,并且据说它与systemd休眠一起工作。但是,我无法使其恢复,所以改用了uswsusp(用户空间软件挂起)。以下是我在Ubuntu 17.04/17.10上使用的步骤。

创建交换文件

创建一个格式化的4GiB交换文件,并将其挂载并添加到/etc/fstab中的命令:

sudo fallocate -l 4g /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
echo '/swapfile swap swap defaults 0 0' | sudo tee -a /etc/fstab

验证交换文件分区

sudo findmnt -no SOURCE,UUID -T /swapfile
> /dev/sda1 11cc33ee-1234-abcd-1234-ddeeff112233

安装用户空间软件挂起(uswsusp)
sudo apt install uswsusp

配置uswsusp

创建/etc/uswsusp.conf并重新创建initramfs

sudo dpkg-reconfigure -pmedium uswsusp
  • 对于 '继续没有有效的交换空间?' (向导尚未设置交换文件。)
  • 选择交换文件所在的分区,与上面的findmnt的详细信息进行交叉参考。(注意:不是交换文件本身

注意:手动对/etc/uswsusp.conf进行的任何更改都需要使用以下命令重新创建initramfs

sudo update-initramfs -u

测试 uswusp 休眠

sudo s2disk

在休眠和恢复时,屏幕上应该显示“快照”消息。
使用systemd休眠时,请使用s2disk命令来替代默认的systemd休眠命令,通过覆盖“systemd-hibernate.service”来实现。
sudo systemctl edit systemd-hibernate.service

在打开的文本编辑器中,输入以下文本(空白的ExecStart是必需的)。然后保存并退出。
[Service]
ExecStart=
ExecStart=/usr/sbin/s2disk 
ExecStartPost=/bin/run-parts -a post /lib/systemd/system-sleep

这将创建一个具有覆盖详细信息的/etc/systemd/system/systemd-hibernate.service.d/override.conf文件。

测试systemd休眠:

systemctl hibernate 

请注意:要检查是否已创建、加载并且没有错误的systemd override.conf,请运行以下命令:
systemctl status systemd-hibernate.service

参考资料:


这个说明似乎能够运行,但是系统休眠对我来说还不能正常工作。你有没有自己测试过?应该将 ExceStart=run-parts -a post /lib/systemd/system-sleep 改为 ExceStartPost=run-parts -a post /lib/systemd/system-sleep - Joaquín Aramendía
已进行测试并且工作正常,但可能需要添加该部分。我已更新我的答案。 - Cas
这看起来不太对劲。systemctlenable 选项会从 /etc/systemd/system/ 创建一个符号链接到 /lib/systemd/system/。你不应该直接复制到 /etc/systemd/system/ - Auspex
是的,如果你要覆盖现有的服务,你应该将服务文件放在/etc/systemd/system目录下。你可以参考这个链接:https://wiki.archlinux.org/index.php/systemd#Writing_unit_files - Cas
话虽如此,在systemd.unit文档底部指定了使用conf文件的替代方法,但仍然建议使用其中任何一种方式。 - Cas
@Auspex 你可能会感兴趣,我已经简化了使用覆盖配置文件的说明。 - Cas
2我没能让这个工作起来。我设置了交换文件,安装并配置了uswsusp,但是sudo s2disk只记录了“保存快照”,然后关闭显示器,停顿约5分钟,然后关机。然而,当我重新启动后,它像没有休眠一样正常启动。 - m93a
我还需要在/etc/default/grub中的GRUB_CMDLINE_LINUX_DEFAULT="resume=/dev/sdaX quiet splash"中添加resume=/dev/sdaX,以使此工作生效(更新grub后也要运行sudo update-grub)。其中sdaX是具有交换文件的分区。来源:https://wiki.debian.org/Hibernation/Hibernate_Without_Swap_Partition - Martin Becker
非常感谢,这对我在Ubuntu 18.04上起作用了。此外,我使用了这个扩展来在电源菜单中添加休眠按钮,来源于这篇文章:http://ubuntuhandbook.org/index.php/2018/05/add-hibernate-option-ubuntu-18-04/ 而且,我还将我的电源按钮默认设置为按下后进入休眠状态。在我的5400转每分钟的硬盘上速度有点慢,但还可以接受。 - Mr.Hunt
出于某种原因,即使我按照这个教程的步骤进行操作,我仍然会收到 sudo s2disk s2disk: Could not use the resume device (try swapon -a). Reason: No such device 的提示。 - user1915011
1我认为这行代码 sudo findmnt -no SOURCE,UUID -T /mnt/4GiB.swap 应该改成 sudo findmnt -no SOURCE,UUID -T /swapfile - user1915011
使用fallocate来创建交换文件是不推荐的,因为它可能导致创建一个带有空洞的文件,建议使用dd代替。点击此处了解更多信息。 - ManSamVampire

我已经快速阅读了教程,如果我理解正确的话,你只需要在Linux命令行中指定“resume”选项。使用Grub2非常简单,而且你的更改将始终被保留。你需要编辑“/etc/default/grub”文件,具体来说是这一行:
GRUB_CMDLINE_LINUX="resume=... resume_offset=..."

之后,运行sudo update-grub使更改生效。

更改GRUB_CMDLINE_LINUX不会影响您拥有的其他Linux安装(因为/etc/grub.d/30_os-prober不使用此变量)。

关于您遇到的问题:交换文件分区是否加密?如果是,则无法休眠。如果不是,则filefrag -v /swapfile的输出可能会有所帮助。


谢谢!我会尝试的。但是我认为这行代码将配置所有Linux条目在grub中。 - tfmoraes
别忘了:在修改/etc/default/grub之后,你需要运行update-grub命令。 - JanC
为什么你不想将这个添加到所有Linux条目中呢? - JanC
@JanC:因为我可能安装了其他的Linux发行版。 - tfmoraes
如果你查看/etc/grub.d/30_os-prober,你会发现GRUB_CMDLINE_LINUX没有被使用,所以改变它不会影响其他Linux安装。 - Andrea Corbellini
嗨Andrea。不,我的带有swapfile的分区没有加密。只是补充一下:filefrag不起作用(至少在这里),一个替代命令是swap-offset。谢谢! - tfmoraes
我的怀疑是你的交换文件碎片化了,所以在从休眠状态启动时系统读取了错误的数据。不幸的是,只有filefrag知道答案。为什么它不起作用呢? - Andrea Corbellini
Andrea,我不知道为什么,在filefrag的输出中没有“第一个块”。在教程中,我提到了swap-offset作为filefrag的替代方法。我不认为交换文件是碎片化的,它是完整创建的,不符合必要条件。而且我注意到当它休眠时,会有关于在交换文件中写入的输出信息。 - tfmoraes
我在Launchpad上发布了一个错误报告 https://bugs.launchpad.net/ubuntu/+source/uswsusp/+bug/663200 - tfmoraes
交换文件的分区是否加密?如果是的话,休眠功能将无法使用。这似乎不是真的。我正在运行Kubuntu 18.04,根分区采用LUKS加密。然而,在添加了"resume=... resume_offset=..."之后,我的休眠功能可以正常工作。 - didi_X8
1@didi_X8 那条评论是8年前写的。自那时以来,情况可能已经发生了变化。谢谢你的分享。 - Andrea Corbellini

Ubuntu 22.04

uswusp已被弃用。您仍然可以在Ubuntu <= 20.04中使用它。要在Ubuntu 22.04上查找交换偏移量:

  • 运行sudo filefrag -v /swapfile
  • "physical_offset"列中,复制第一行的数字(不包括点号!)。 值示例:1234567

来源:如何在Ubuntu上启用休眠(使用交换文件)

对于Ubuntu 22.04,所有其他步骤保持不变,因此您可以按照其他答案操作,除了安装和使用uswusp


这个过程对我不起作用。我既有交换文件又有交换分区。系统最初配置为休眠到交换分区(这个方式是有效的)。现在我尝试让它休眠到交换文件(因为交换分区不够大)。我已经在/etc/default/grub中更新了交换文件的UUID和偏移量,并创建了适当的/etc/initramfs-tools/conf.d/resume文件。然而,sudo update-initramfs -u -k all命令产生了原始的输出: "I: The initramfs will attempt to resume from /dev/sdb1 I: Set the RESUME variable to override this." 我无法弄清楚如何覆盖这个设置。 - undefined

由于之前的回答似乎没有完全涵盖所有方面,这里是我为了在Debian Bullseye上顺利运行一切所采取的步骤,因此应该适用于使用grub2而不使用已经不存在的软件包uswsusp和整个s2*功能的Ubuntu 22及更高版本。我认为这也适用于较低版本:

  1. 假设我们已经准备好了名为/tmpdisk/system/swap.file的交换文件。如何准备它,请参考this answer
  2. 通过确定交换文件在其所在分区上的第一个片段来获取关于交换偏移量的信息,例如:

# filefrag -v /tmpdisk/system/swap.file

获得结果后,偏移量就是第一个物理偏移片段的编号。

Filesystem type is: ef53
File size of /tmpdisk/system/swap.file is 68719476736 (16777216 blocks of 4096 bytes)  
ext:     logical_offset:        physical_offset: length:   expected: flags:
  0:        0..   63487:      34816..     98303:  63488:             
  1:    63488..  126975:     100352..    163839:  63488:      98304:
  2:   126976..  190463:     165888..    229375:  63488:     163840:
 …  

通过简单地发出以下命令,可以获取相同的信息:
# filefrag -v /tmpdisk/system/swap.file | awk '$1=="0:" {print substr($4, 1, length($4)-2)}'
直接获取偏移值:
34816

  1. 查找具有交换文件的分区的UUID(在我的情况下,文件/tmpdisk/system/swap.file位于分区上,挂载在挂载点/tmpdisk上):

# findmnt -no UUID,SOURCE -T /tmpdisk
结果类似于这样:
6b127402-e917-4ab0-9490-00faa74e88e5 /dev/sdb1

  1. 使用获取到的分区UUID和/etc/default/grub中的偏移量:

GRUB_CMDLINE_LINUX_DEFAULT="resume=UUID=6b127402-e917-4ab0-9490-00faa74e88e5 resume_offset=34816"

使用在/etc/initramfs-tools/conf.d/resume中获取的交换文件分区的UUID和偏移量,按照以下方式进行操作:
resume=UUID=6b127402-e917-4ab0-9490-00faa74e88e5
resume_offset=34816

更新所有使用的内核的ramdisks:
# update-initramfs -u -k all
更新grub:
# update-grub
就这样...
注意:UUID = 6b127402-e917-4ab0-9490-00faa74e88e5和resume_offset = 34816的值将在每个系统上独立。

我花了好几个小时来让我的KDE(neon)在关机菜单中拥有休眠选项,自从我解决了这个问题,我决定在这里分享一下。 如果你正在使用KDE,你应该创建一个名为"/etc/polkit-1/localauthority/10-vendor.d/hibernate.pkla"的文件,并将以下内容复制进去:
[Re-enable hibernate by default in upower]
Identity=unix-user:*
Action=org.freedesktop.upower.hibernate
ResultActive=yes

[Re-enable hibernate by default in logind]
Identity=unix-user:*
Action=org.freedesktop.login1.hibernate;org.freedesktop.login1.handle-hibernate-key;org.freedesktop.login1;org.freedesktop.login1.hibernate-multiple-sessions;org.freedesktop.login1.hibernate-ignore-inhibit
ResultActive=yes

我这里有:https://forum.kde.org/viewtopic.php?f=309&t=135294

已经过了几年了,但是提醒一下 @user2275455,你发布的链接用于设置 polkit 下休眠选项的来源已经不存在了。当我点击时,收到了文件未找到的消息。再见。 - Terence Golightly