未启用交换的休眠

jos*_*sch 9 linux hibernate ssd swap systemd

我的笔记本电脑有足够的内存和 SSD。为了不磨损 SSD,我不想一直使用交换。如果一个进程使用了​​太多的 RAM 以至于它不得不开始使用交换,那么它显然是行为不端,应该被内核杀死。但我也希望能够休眠或混合睡眠我的笔记本电脑。目前似乎很难让这样的设置工作。以下是一些注意事项:

  • swappiness如果应用程序请求太多内存,即使设置为 0 也将允许使用交换。似乎没有办法启用交换但阻止内核将其用作交换空间(从而将其效用减少到休眠图像的空间)
  • 禁用交换(临时)将导致诸如systemctl hibernate错误输出之类的命令Failed to hibernate system via logind: Sleep verb not supported
  • 我希望 upower 能够将我的系统置于休眠/混合睡眠状态(因此编写一个启用交换然后启动的包装脚本systemctl hibernate是不够的)

我现在正在寻找实现这一目标的正确方法。一种方法可能是通常禁用我的交换分区,然后swapon通过 systemd 在正确的点(以及swapoff在系统唤醒后)。但我不知道如何实现这一目标。

另一种方法是使用交换文件,但与使用交换分区相比,这似乎增加了额外的复杂性。尤其是因为我正在使用全盘加密,而且uswsusp似乎只是我现有交换分区已有的另一层。但也许我遗漏了一些东西,为此目的使用交换文件确实总体上更容易。

小智 6

  1. 当您已经知道系统可能会出现内存不足的情况时禁用交换是一个坏主意。就在内核没有任何内存来分配您的系统时,您的系统可能会非常非常无响应或只是挂起,因此您必须重新启动它。挂起可能会导致数据丢失,有时还会导致文件系统损坏。取决于文件系统,它可以自动修复(ext* 系列、fat 系列、btrfs 和其他一些),但并非每个 FS 都支持此功能。我敢打赌,您可能不喜欢fsck每次出现损坏时都以单用户模式运行...

  2. 内核并没有那么愚蠢,只是将它想要的所有内容写入您的 SSD,它的行为与 HDD 相比有所不同,并尝试使用诸如 TRIM 之类的东西进行最少的写入。对您的驱动器并没有您想象的那么大的危害。

  3. 休眠是一个内置的内核子系统,它在低级别运行。它仍然必须有一个单独的分区大小?RAM 大小使其可以对您的内存进行完整快照。在这里您可以阅读 linux 如何管理休眠。如果没有永久存储内存映像的地方,那么休眠就无法工作。你不能在空中存储数据。没有永久的物理存储 = 没有休眠。所以这也是你的问题的答案:你不能在没有交换的情况下使用休眠 linux

所以,我的观点是,您当然可以不使用交换而仅在休眠时启用它,但前提是您确定系统在 99.9% 的时间都在运行,它有足够的内存来存储所有内容。如果这不是你的情况,你必须使用交换。

现在到可能的解决方案:

当您运行systemctl hibernatesystemd 时,会启动一个名为systemd-hibernate.service的服务。其单元文件通常位于/usr/lib/systemd/system/目录中

在 Debian 上,它看起来像这样:

#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Hibernate
Documentation=man:systemd-suspend.service(8)
DefaultDependencies=no
Requires=sleep.target
After=sleep.target

[Service]
Type=oneshot
ExecStart=/lib/systemd/systemd-sleep hibernate
Run Code Online (Sandbox Code Playgroud)

如您所见,有RequiresAfter选项。我们可以编写另一个单元来启用交换并将其名称添加到这些字段中。我们将其命名为swap-on.service并将其放在/etc/systemd/system/目录中

我们的文件看起来像这样:

# Unit filed is not required in our case so you can skip it
[Unit]
Description=Enable swap partition

[Service]
# This line means that service will be executed just once and immediately stop when
# swapon enables swap on /dev/sda3
Type=oneshot

# Change /dev/sda3 with device named after your swap partition 
# like /dev/sdb3 or /dev/nvme0n1p3
# Use lsblk to determine your swap partition (partition with [SWAP] as mountpoint)
# Of course you can think of a more complex solution like running external script
# that determines your swap partition automatically every time service is executed
# but for now I'll go with simple /dev/sda3
ExecStart=/sbin/swapon /dev/sda3
Run Code Online (Sandbox Code Playgroud)

现在我们应该修改我们系统的systemd-hibernate.service

#  SPDX-License-Identifier: LGPL-2.1+
#
#  This file is part of systemd.
#
#  systemd is free software; you can redistribute it and/or modify it
#  under the terms of the GNU Lesser General Public License as published by
#  the Free Software Foundation; either version 2.1 of the License, or
#  (at your option) any later version.

[Unit]
Description=Hibernate
Documentation=man:systemd-suspend.service(8)
DefaultDependencies=no
Requires=sleep.target swap-on.service <--
After=sleep.target swap-on.service <--

[Service]
Type=oneshot
ExecStart=/lib/systemd/systemd-sleep hibernate
Run Code Online (Sandbox Code Playgroud)

我们在 After 中复制swap-on.service以确保 systemd在交换开启开始休眠反之亦然,并在 Required 中确保如果swap-on.service失败,systemd 不会尝试休眠。

现在,当我们运行systemctl hibernatesystemd 时,运行我们修改过的systemd-hibernate.service,它运行启用交换的swap-on.service。当启用交换并且必需和之后字段都满足 systemd 可以最终使我们的系统休眠。

为了在从休眠状态恢复后使 systemd 禁用交换,我们不需要创建另一个名为swap-off.service 的服务文件,它将与我们的swap-on.service做同样的事情,将它放在与“on”服务相同的目录中(/etc/systemd/system/)。唯一的区别是该服务将运行swapoff而不是swapon

[Unit]
Description=Disable swap partition

[Service]
Type=oneshot

#  again, change /dev/sda3 to your swap partition /dev file
ExecStart=/sbin/swapoff /dev/sda3 <--
Run Code Online (Sandbox Code Playgroud)

下一步是修改位于同一/usr/lib/systemd/system目录中的名为systemd-hibernate-resume@.service 的服务文件:

[Unit]
Description=Resume from hibernation using device %f
Documentation=man:systemd-hibernate-resume@.service(8)
DefaultDependencies=no
BindsTo=%i.device
Wants=local-fs-pre.target
After=%i.device
Before=local-fs-pre.target
ConditionPathExists=/etc/initrd-release

[Service]
Type=oneshot
ExecStart=/lib/systemd/systemd-hibernate-resume %f
Run Code Online (Sandbox Code Playgroud)

我们现在需要修改Before字段。在 local-fs-pre.target旁边添加swap- off.service

...
After=%i.device
Before=local-fs-pre.target swap-off.service <--
ConditionPathExists=/etc/initrd-release
...
Run Code Online (Sandbox Code Playgroud)

现在,当您从休眠状态恢复时,systemd 将在将交换图像加载到内存中后运行swap-off.service(实际上稍后,但这对我们来说并不重要)

请记住,这是一个完全理论上的解决方案,我还没有尝试过,但根据我对 systemd 的了解,这应该可行。由于 UPower 要求 systemd 休眠,正如我所解释的那样运行 systemd-hibernate.service 这可能就是您正在寻找的