我可以在btrfs上使用swap文件吗?

现代的Ubuntu版本默认使用交换文件而不是交换分区。
在5.0 Linux内核之前,无法将交换文件放置在btrfs分区上,否则可能会损坏btrfs文件系统。
现在的内核支持在btrfs分区上使用交换文件。我可以在安装在btrfs上的Ubuntu上使用交换文件吗?可能会有哪些问题?
7个回答

可以在上使用交换文件,但需要注意一些问题。
如果子卷上有一个工作的交换文件,文件系统将无法创建快照。因此,强烈建议将交换文件放在单独的子卷上。
假设当前交换已经关闭,/位于/dev/sda1上,Ubuntu安装在以@为子卷的/上,/home位于以@home为子卷的地方。
  1. /dev/sda1 挂载到 /mnt

    sudo mount /dev/sda1 /mnt
    

    如果运行 ls /mnt,你会看到可能存在的 @@home 和其他子卷。

  2. 创建一个新的 @swap 子卷。

    sudo btrfs sub create /mnt/@swap
    
  3. /mnt 卸载 /dev/sda1

    sudo umount /mnt
    
  4. 在计划挂载 @swap 子卷的位置创建 /swap 目录。

    sudo mkdir /swap
    
  5. @swap 子卷挂载到 /swap

    sudo mount -o subvol=@swap /dev/sda1 /swap
    
  6. 创建交换文件。

    sudo touch /swap/swapfile
    
  7. 为该文件设置 600 权限。

    sudo chmod 600 /swap/swapfile
    
  8. 禁用该文件的写时复制(COW)。

    sudo chattr +C /swap/swapfile
    
  9. 将交换文件的大小设置为 4G,这里是一个示例。

    sudo dd if=/dev/zero of=/swap/swapfile bs=1M count=4096
    
  10. 格式化交换文件。

    sudo mkswap /swap/swapfile
    
  11. 启用交换文件。

    sudo swapon /swap/swapfile
    
现在新的交换应该可以工作了。
你还需要更新/etc/fstab以便在启动时挂载所有这些。在那里添加两行:
UUID=XXXXXXXXXXXXXXX /swap btrfs subvol=@swap 0 0
/swap/swapfile none swap sw 0 0

UUID 是你的 /dev/sda1 之一。

交换文件不能位于任何类型的 btrfs RAID 上。

欢迎评论和建议。


请向我们这些从未尝试过btrfs的人解释一下:除了像这样的交换文件之外,有哪些替代方案?相比于其他方法,这种方法有什么优势? - sudodus
如果你只是在btrfs上有一个交换文件,就像安装程序默认创建的那样,快照将无法工作。我提到过这一点。这是将其移动到另一个子卷的主要目的。使用btrfs的主要原因之一就是具备快照功能。 - Pilot6
在同一驱动器上是否可以有一个单独的交换分区(与btrfs分区并存)? - sudodus
没有问题可以有一个交换分区,但是Ubuntu安装程序现在创建一个交换文件。这给使用快照和像timeshift这样的软件的人带来了麻烦。 - Pilot6
那么,当使用btrfs时,它是最好的选择,至少在原始分区过程中没有创建交换分区的情况下是这样吗?或者如果用户需要更多的交换空间呢? - sudodus
安装程序通常会创建一个交换文件,除非进行手动分区。为了能够使用快照,你有几个选择:不使用交换空间,拥有一个交换分区,或者在一个单独的子卷上拥有一个交换文件。我解释了如何制作后者。 - Pilot6
谢谢;-) 我对你的回答很满意。你想继续这个对话,还是希望我删除我的问题? - sudodus
让我们在聊天中继续这个讨论。 - Pilot6
我刚刚尝试了一下,但是出现了一个错误:sudo swapon /swap/swapfile swapon: /swap/swapfile: swapon 失败:无效的参数我甚至确保没有使用 fallocate - YamiYukiSenpai
@YamiYukiSenpai 你在使用RAID吗?RAID上不能有交换文件。 - Pilot6
不,我成功解决了。不得不从头开始,并确保在增加交换文件之前禁用写时复制(或者是在创建文件之前禁用COW)。 - YamiYukiSenpai

你可以在btrfs上进行交换,只要你注意btrfs文档中关于此的说明。
filesystem - must be only single device
filesystem - must have only single data profile
swapfile - the containing subvolume cannot be snapshotted
swapfile - must be preallocated (i.e. no holes)
swapfile - must be NODATACOW (i.e. also NODATASUM, no compression)

更多信息请参见源代码:

流行的回答是正确的,除了你不应该使用fallocate(1)来分配交换文件的空间。这可能会创建与交换文件使用不兼容的文件系统“空洞”(因为交换需要100%连续的空间,而交换空间内部不使用文件系统)。 相反,你应该使用“dd if=/dev/zero ...”命令。 在这个帖子上看到这个回答:fallocate vs dd for swapfile? mkswap(8)和swapon(8)的手册都明确不建议使用fallocate(1)在文件系统上创建交换文件。

2是的,但是那个链接中的答案只会创建一个1Mb的交换文件。如果要创建一个8Gb的交换文件,你需要执行以下命令:"sudo dd if=/dev/zero of=/swap/swapfile bs=1M count=8192" - bshor
1我从allocate改成了dd。 - Pilot6
这还是真的吗?Btrfs文档在他们的示例中使用了fallocate:https://btrfs.readthedocs.io/en/latest/Swapfile.html - falcojr

请注意,对于配备NVME固态硬盘的现代系统/笔记本电脑,您将使用/dev/nvme0n1p2而不是/dev/sda1,并且应忽略/dev/nvme0n1p1,因为那是您安装操作系统时创建的efi引导磁盘。
此外,建议添加两个挂载选项:“defaults”和“noatime”。Defaults会自动加载驱动器(SSD、HDD)的挂载选项。Noatime将防止文件在仅打开时被写入。
UUID=XXXXXXXXXXXXXXX /swap btrfs defaults,noatime,subvol=@swap 0 0
/swap/swapfile none swap sw 0 0

1noatime不是必需的,因为relatime是默认值,而且只有在文件被写入后才会更新。自从内核将默认值更改为relatime以来,这已经不再是一个问题了大约15年。我不确定你是否需要默认值,或者交换文件是否受益于lazytimecompress。不过,它很可能会有所帮助。 - xenoterracide
1如果还有其他选项的话,“默认值”就没有什么意义。它只在没有其他选项时使用。 - Pilot6

如果您的分区已加密(LUKS),挂载点位于/dev/mapper,例如:
/dev/mapper/nvme0n1p5_crypt /swap btrfs defaults,noatime,subvol=@swap 0 0
/swap/swapfile none swap sw 0 0 here

要获取uuids,运行blkid命令:
/dev/mapper/nvme0n1p5_crypt: UUID="06c8c73c-1cc4-477b-a687-6c21697d645d"   UUID_SUB="7f884b26-d76e-49db-9959-311fa2a5dd20" TYPE="btrfs"
/dev/nvme0n1p1: UUID="8090a824-63fa-4087-a948-89cca1a369cd" TYPE="ext2" PARTUUID="887ff27b-01"
/dev/nvme0n1p5: UUID="46405308-2ed1-40f3-a86b-906f1118970b" TYPE="crypto_LUKS" PARTUUID="887ff27b-05"

然后将mapper位置替换为特定的uuid
UUID=06c8c73c-1cc4-477b-a687-6c21697d645d /swap btrfs   defaults,noatime,subvol=@swap 0 0 
/swap/swapfile none swap sw 0 0

自从6.1版本以来,可以通过一个单独的命令创建交换文件
我将尝试测试以下情况的影响(例如,是否应该将btrfs交换文件挂载在交换文件子卷中,以便仍然可以在父卷上执行快照操作?)
请注意,不支持在包含交换文件的子卷上进行快照,因为存在以下快照要求。
subvolume - cannot be snapshotted if it contains any active swapfiles

使用默认值8G,但如果已经设置了,则使用已设置的值。
btr_swap_size=${btr_swap_size:=8G}
btrfs subvolume create SWAP
btrfs filesystem mkswapfile --size btr_swap_size /SWAP/swapfile
swapon /swapfile

一个小脚本
btr_swap_size=${btr_swap_size:=8G}
btr_swap_subvolume=${btr_swap_subvolume:="/SWAP"}
btr_swap_file=${btr_swap_subvolume}/SWAP-"${btr_swap_size}".img
swapon -s|grep -q "$swapfile" /etc/fstab
if [ $? -ne 0 ]; then
# if not then create it
    echo '$swapfile not found. Adding $swapfile.'

    btrfs subvolume create ${btr_swap_subvolume}
    btrfs filesystem mkswapfile --size "${btr_swap_size}" "${btr_swap_file}"
    swapon "${btr_swap_file}"
    echo '/$swapfile none swap defaults 0 0' >> /etc/fstab
else
    echo '$swapfile found. No changes made.'
fi

我认为你可以通过这个命令创建一个子卷,并在那里设置一个交换空间。 - undefined
我老实说,当我写这个回答的时候,我没有意识到我重复了https://askubuntu.com/a/1457889/59660的条件。但是我希望能提供一个可粘贴的答案,希望能为这个问题增加一些价值... - undefined
有一些错误。如果不指定设备,btrfs subvolume create SWAP 将无法工作。此外,您需要挂载它。 - undefined

对我来说,最好的答案没弄明白。我一直收到错误提示:
$ sudo swapon -v --show /swap/swapfile

swapon: /swap/swapfile: swapon失败:无效的参数
对我来说,解决方案是从https://btrfs.readthedocs.io/en/latest/btrfs-man5.html#swapfile-support中找到的。
# truncate -s 0 swapfile
# chattr +C swapfile
# fallocate -l 2G swapfile
# chmod 0600 swapfile
# mkswap swapfile
# swapon swapfile

/etc/fstab:
/swap/swapfile              none            swap    sw              0       0

1这个答案看起来不正确。不清楚交换文件的位置在哪里。是在/swapfile还是/swap/swapfile - Pilot6