让 ArchLinux 休眠到交换文件
Linux 使用交换分区来休眠,首先冻结所有进程并申请足够的交换内存(位于磁盘),把当前内存都存进去。
然后下次启动时,initramfs 会直接加载上次休眠时的内存状态,跳过内核的 init 过程。
因此首先需要有足够大的交换分区或交换文件;再把内核指向到休眠的分区上;最后再配置 initramfs 让它加载休眠的内存文件。
官方文档请参考 Power_management/Suspend_and_hibernate#Hibernation
,
本文细述如何休眠到交换文件,并对其中一些概念和细节进行了解释。
在本文讨论的范围内, 挂起
(suspend)是指冻结当前的进程,保留它们的内存,并把几乎除了内存之外的设备都断电。 休眠
(hibernate)是指把挂起后的内存写入磁盘并完全关机。 锁定
(lock)则只是显示一个模态的全屏软件输入正确的密码才能退出。
交换文件
在安装系统前需要创建交换分区,现在的机器普遍内存较大不太需要交换分区来扩展内存空间,
而且磁盘一般使用读写快速但读写次数有限 SSD,因此 Harttle 的交换分区也很小根本不够用来休眠。
你可以通过 swapiniss 来让你的交换分区只用于休眠。
所以我们用交换文件来替代交换分区,在创建交换文件之前首先需要知道系统休眠需要多大空间。
可以从 sysfs 来看查看:
cat /sys/power/image_size
可以参考 [官方教程][https://wiki.archlinux.org/index.php/Swap#Swap_file] 来创建:
# 按照你需要的大小创建,bs * count 是最终文件大小 dd if=/dev/zero of=/swapfile bs=1M count=4096 status=progress chmod 600 /swapfile # 检查大小和权限 ls -l /swapfile # 初始化交换文件并立即应用到系统 mkswap /swapfile swapon /swapfile # 在 /etc/fstab 中写入以下内容,交换文件会在重启后生效 /swapfile none swap defaults 0 0
重启后通过 swapon -s
来检查是否生效:
> sudo swapon -s Filename Type Size Used Priority /swapfile file 12582908 0 -2
让内核找到交换文件
我们需要设置 resume 和 resume_offset 两个内核参数,告诉内核在挂起时把内存写入到哪里。
filefrag -v /swapfile | awk '{ if($1=="0:"){print $4} }'
可以通过 cat /proc/cmdline
来查看当前的内核参数,但是在哪里设置取决于你的 Boot Loader。
以 rEFInd 为例,打开 /boot/refind_linux.conf 写入 resume 和 resume_offset:
"Boot with standard options" "ro root=/dev/sda3 resume=/dev/sda3 resume_offset=3192832"
重启后用 journalctl
或 dmesg
来找到写入休眠镜像的日志:
Oct 19 13:57:56 harttle.arch.mac kernal: PM: Creating hibernation image: Oct 19 13:57:56 harttle.arch.mac kernel: PM: Need to copy 596422 pages Oct 19 13:57:56 harttle.arch.mac kernel: PM: Normal pages needed: 596422 + 1024, available pages: 1477067
如果看到这样的错误说明设置有误,请检查你交换文件所在分区和 filefrag 给出的偏移量:
Oct 19 13:57:56 harttle.arch.mac kernal: PM: Image not found (code -22)
让 initramfs 加载休眠的内存
initramfs 是由 Boot Loader 直接加载的一个早期的用户空间,其中已经加载了一些内核模块。
它会进行设备初始化、挂载文件系统、运行磁盘检查等工作,之后再交给内核的 init 过程。
加载休眠的内存也是它的工作,但 ArchLinux 默认并未开启,需要去 /etc/mkinitcpio.conf 中添加 resume 钩子:
HOOKS=(base udev resume autodetect modconf block filesystems keyboard fsck)
注意因为 resume 参数用到了磁盘设备名称,resume 需要写在 udev 之后。
然后重新编译 initramfs(就像更新内核时一样):
# 默认使用当前系统的内核,如果你现在位于启动盘的系统则需要指定宿主环境上的内核版本。 mkinitcpio -p linux
至此配置工作都完成了,通过 systemctl hibernate
来休眠,再按下电源键开机来检查休眠功能是否正常。
自动休眠
自动休眠和其他电源管理功能,由很多不同的软件和配置方式来实现。为避免混淆先介绍几个常见的软件:
systemd-logind:ArchLinux 的默认安装包含了 systemd,其中的 systemd-logind 是自动启用的。
它包含了一些非常简单的电源管理功能,比如按下电源按钮时关机、笔记本合上盖子时挂起。
因此 ArchLinux 装好之后就基本可以用了。
acpid: acpid
是一个比较基础的电源管理工具,工作方式是响应 ACPI
事件,做相应的处理比如关机还是休眠。注意 acpid 只是电源管理工具,ACPI 是设备配置接口跟它没关系。
tlp: tlp
是一个比较无脑的电源管理工具,提供类似电池模式、电源模式、性能优先这样级别的配置。
xss-lock, xidle
, xautolock
:这些是 X11 下的工具用来在用户无操作时执行挂起等操作,有些还会监听 ACPI 事件,这样在 suspend 时屏幕也能锁定。
无操作自动休眠
自动休眠和自动挂起需要桌面环境(DE)或者 X11 软件的支持。
如果你在用 Gnome 或 KDE 在控制面板中配置后,会把 idle 信息报告给 systemd-logind,后者接管具体操作。
如果你像 Harttle 一样没有桌面系统和登录管理器的话,
需要安装一个类似 xss-lock, xidle 这样的工具来靠 X11 事件计时,
然后调用 systemctl hibernate
或 systemctl syspend
。
先挂起后休眠
合上盖子后 systemd-logind 的默认行为是挂起,可以在 /etc/systemd/logind.conf
中把它重新设置为休眠,或先挂起再休眠:
HandleLidSwitch=suspend-then-hibernate
挂起后休眠前的时间可以在 /etc/systemd/sleep.conf 中设置:
HibernateDelaySec=15min
除了合上盖子之外,其他场景也可以直接调用 systemctl suspend-then-hibernate
。
电量低自动休眠
这件事情需要具体的软件来做,或者直接安装 tlp
并启动 tlp, tlp-sleep 两个 systemd 服务。
下面提供一个简单的 udev 规则,在电量小于等于 5% 时休眠:
SUBSYSTEM=="power_supply", ATTR{status}=="Discharging", ATTR{capacity}=="[0-5]", RUN+="/usr/bin/systemctl hibernate"
把它写入 /etc/udev/rules.d/99-lowbat.rules,重启即可生效。