0%

Hibernation with Btrfs Subvolume

本主旨在记录Linux的Btrfs文件系统下开启休眠的过程,及使用systemd-boot替代grub启动时的设置。

Introduction

计算机有S0,S1,S2,S3,S4,S5状态。S4是指休眠(Hibernation),S5即关机(Shutdown)。
S0: 正常的工作状态,所有设备全开;
S1: Standby,除了通过CPU时钟控制器将CPU关闭之外,其他的部件仍然正常工作;
S2: Standby,CPU处于停止运作状态,总线时钟也被关闭,但其余的设备仍然运转,和 S1 几乎一样;
S3: Suspend to RAM,系统运行中的所有资料保存不动,进入假关机,除内存需电源供电外,其它设备全部停止供电;
S4: Suspend to Disk,把内存中的资料完整的保存在硬盘中,重开机时直接从硬盘完整的读到RAM。使用这种模式,硬盘需留出一完整的连续的空间;
S5: Shutdown。
综上,待机下电池一旦耗尽,内存的内容将丢失,而休眠可以在断电情况下保存运行状态,即将主存内容保存在硬盘中的某个区域,然后断电。Windows下的快速启动遇刺类似。
由于正在使用Btrfs,根据Archwiki所述开启Linux休眠。在这里选择休眠到文件(Swap To File);

Create Subvolume and Swap File

  1. 我有两块SSD,都使用了btrfs,所以在其中一块专门存储数据的SSD上创建子卷及交换文件。因为所谓存储盘的原因,我开启了COW,而休眠文件需要关闭COW,且由于Archwiki1提到的原因,nodatacow挂载选项可能没有效果,所以考虑直接对休眠文件运行chattr +C来控制

within a single file system, it is not possible to mount some subvolumes with nodatacow and others with datacow. The mount option of the first mounted subvolume applies to any other subvolumes.

1
2
3
[root@Linux]# export fs_uuid=$(findmnt /Misc -o UUID -n) && echo $fs_uuid
[root@Linux]# btrfs subvolume create /Misc/@swap
[root@Linux]# mount -m -U $fs_uuid -o subvol=@swap,nodatacow /swap
  1. 创建交换文件并且设置权限
1
2
3
4
5
6
[root@Linux]# touch /swap/swapfile
[root@Linux]# chattr +C /swap/swapfile
[root@Linux]# export swp_size=$(echo "$(grep "MemTotal" /proc/meminfo | tr -d "[:blank:],[:alpha:],:") * 1 / 1000" | bc ) && echo $swp_size
[root@Linux]# dd if=/dev/zero of=/swap/swapfile bs=1M count=$swp_size status=progress
[root@Linux]# chmod 0600 /swap/swapfile
[root@Linux]# mkswap /swap/swapfile
  1. 在/etc/fstab文件中添加相应自动挂载条目
1
2
3
4
5
6
7
[root@Linux]# umount /swap
[root@Linux]# echo -e "UUID=$fs_uuid\t/swap\tbtrfs\tsubvol=@swap,nodatacow,noatime,nospace_cache\t0\t0" | sudo tee -a /etc/fstab
[root@Linux]# echo -e "/swap/swapfile\tnone\tswap\tdefaults\t0\t0" | sudo tee -a /etc/fstab
[root@Linux]# systemctl daemon-reload
[root@Linux]# mount /swap
[root@Linux]# swapon -a
[root@Linux]# swapon -s

Enable Hiberation with grub or systemd-boot

  1. 查看btfs下swapfile真实文件地址及Service文件编写
1
2
3
4
5
6
7
[root@Linux]# export swp_uuid=$(findmnt -no UUID -T /swap/swapfile) && echo $swp_uuid
[root@Linux]# curl -s "https://raw.githubusercontent.com/osandov/osandov-linux/master/scripts/btrfs_map_physical.c" > bmp.c
[root@Linux]# gcc -O2 -o bmp bmp.c
[root@Linux]# swp_offset=$(echo "$(sudo ./bmp /swap/swapfile | egrep "^0\s+" | cut -f9) / $(getconf PAGESIZE)" | bc) && echo $swp_offset
[root@Linux]# sudo mkdir -pv /etc/systemd/system/{systemd-logind.service.d,systemd-hibernate.service.d}
[root@Linux]# echo -e "[Service]\nEnvironment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1" | sudo tee /etc/systemd/system/systemd-logind.service.d/override.conf
[root@Linux]# echo -e "[Service]\nEnvironment=SYSTEMD_BYPASS_HIBERNATION_MEMORY_CHECK=1" | sudo tee /etc/systemd/system/systemd-hibernate.service.d/override.conf
  1. 修改initramfs生成配置文件及开机启动配置文件
1
2
[root@Linux]# echo -e "HOOKS+=( resume )" | sudo tee -a /etc/mkinitcpio.conf
[root@Linux]# mkinitcpio -P

若使用grub则修改/etc/default/grub配置文件,然后重新生成grub.cfg

1
2
[root@Linux]# echo -e "GRUB_CMDLINE_LINUX_DEFAULT+=\" resume=UUID=$swp_uuid resume_offset=$swp_offset \"" | sudo tee -a /etc/default/grub
[root@Linux]# grub-mkconfig -o /boot/grub/grub.cfg

若使用systemd-boot2则,直接修改启动项配置/boot/loader/entries/xxx.conf即可:

1
[root@Linux]# echo -e "options resume=UUID=$swp_uuid resume_offset=$swp_offset " | sudo tee -a /boot/loader/entries/arch.conf

then reboot,test withsudo systemctl hibernate

参考