Archlinux休眠设置

2017-03-11 更新:

  1. 优化部分文字描述;
  2. 默认情况下禁用 swap 分区, 当执行休眠操作时先启用 swap 分区, 然后再执行休眠操作(给 /usr/bin/{swapon,swapoff} 添加 S 权限位, 以便普通用户修改 swap 配置);

基础配置

因为笔记本只有 180GB 的固态硬盘, 当初安装系统就使用 swap 文件代替 swap 分区. 首先检查下 swap 文件大小, 确定其足以 dump 整个内存:

$ swapon -s
文件名                          类型            大小    已用    权限
/swapfile                     file            8388604 1467244 -1

然后就是配置 GRUB 启动参数:

title Arch
kernel  (hd0,5)/boot/vmlinuz-linux root=/dev/sda6  rw quiet resume=/dev/sda6 resume_offset=2537472
initrd  (hd0,5)/boot/initramfs-linux.img

其中 resume 参数是 swap 文件所在分区, resume_offset 是真正存储内存 dump 数据(physical_offset)的偏移, 可通过 filefrag 命令获取:

# filefrag -v /swapfile
Filesystem type is: ef53
File size of /swapfile is 8589934592 (2097152 blocks of 4096 bytes)
 ext:     logical_offset:        physical_offset: length:   expected: flags:
   0:        0..       0:    2537472..   2537472:      1:
   1:        1..    2047:    2537473..   2539519:   2047:             unwritten
   2:     2048..    4095:    2899968..   2902015:   2048:    2539520: unwritten
   3:     4096..    6143:    2914304..   2916351:   2048:    2902016: unwritten
....

在我的电脑上, resume_offset 值就是 2537472.

测试和问题分析

完成以上准备工作后执行 systemctl hibernate 命令执行休眠(dump 内存到 swap 文件并关机), 重启时发现, 只有少数几次能够从休眠中复原环境, 大多数是恢复失败直接进入登录页面(另外, 4.4 版本的内核对休眠支持有bug, 重启后黑屏, 升级 4.9 后解决该问题). 查看日志发现:

2月 25 20:10:07 hostname systemd[1]: Starting Hibernate...
2月 25 20:10:39 hostname kernel: PM: Hibernation image not present or could not be loaded.
2月 25 20:10:39 hostname kernel: PM: Hibernation image partition 8:6 present
2月 25 20:10:39 hostname kernel: PM: Hibernation image not present or could not be loaded.

为什么会提示 Hibernation image not present or could not be loaded, 但为什么有时候又能成功呢? 猜测在某些情况下将内存 dump 到 swap 文件时出错了.

查看 Wiki 发现有个 /sys/power/image_size 参数配置, Wiki 说:

/sys/power/image_size 用来控制将内存 dump 到硬盘时所占空间的大小. 在 dump 内存时, 所占用的硬盘空间不会超过 /sys/power/image_size 的大小. 如果内存数据太多, 那就只会 dump 最小的镜像到硬盘. 如果该文件值为 0, 则在 dump 内存时尽可能压缩数据占用最少的硬盘空间(上限是 swap 分区/文件的大小). 该文件的默认值是内存的 2/5 .

看了上面这段描述, 猜测是/sys/power/image_size 值过小致使内存 dump 文件不完整, 从而导致无法从休眠中启动恢复环境. 于是将其修改为 0:

$ sudo tee /sys/power/image_size <<< 0
[sudo] username 的密码:
0

在内存使用率超过 2/5 的情况下测试通过.

然而, 重启后 /sys/power/image_size 配置值又恢复为默认的内存大小的 2/5, System: Temporary Files 介绍了一种方法:

新建文件 /etc/tmpfiles.d/modify_power_image_size.conf, 内容为:

w /sys/power/image_size - - - - 0

重启后确认已生效:

$ cat /sys/power/image_size
0

扩展配置

通过 acpid 捕获合上笔记本屏幕事件

安装并启用 acpid:

sudo pacman -S acpid
sudo systemctl enable acpid

编辑 /etc/acpi/handler.shbutton/lid 部分, 当合上笔记本屏幕时, 执行锁屏和挂起(睡眠)操作:

DISPLAY=:0.0 su -c - your_username /usr/bin/slimlock &
systemctl suspend

处理合上笔记本屏幕事件:

    button/lid)
        case "$3" in
            close)
                logger 'LID closed'
                DISPLAY=:0.0 su -c - your_username /usr/bin/slimlock &
                systemctl suspend
                ;;
            open)
                logger 'LID opened'
                ;;
            *)
                logger "ACPI action undefined: $3"
                ;;

/etc/acpi/handler.sh 完整文件:

#!/bin/bash
# Default acpi script that takes an entry for all actions

case "$1" in
    button/power)
        case "$2" in
            PBTN|PWRF)
                logger 'PowerButton pressed'
                ;;
            *)
                logger "ACPI action undefined: $2"
                ;;
        esac
        ;;
    button/sleep)
        case "$2" in
            SLPB|SBTN)
                logger 'SleepButton pressed'
                ;;
            *)
                logger "ACPI action undefined: $2"
                ;;
        esac
        ;;
    ac_adapter)
        case "$2" in
            AC|ACAD|ADP0)
                case "$4" in
                    00000000)
                        logger 'AC unpluged'
                        ;;
                    00000001)
                        logger 'AC pluged'
                        ;;
                esac
                ;;
            *)
                logger "ACPI action undefined: $2"
                ;;
        esac
        ;;
    battery)
        case "$2" in
            BAT0)
                case "$4" in
                    00000000)
                        logger 'Battery online'
                        ;;
                    00000001)
                        logger 'Battery offline'
                        ;;
                esac
                ;;
            CPU0)
                ;;
            *)  logger "ACPI action undefined: $2" ;;
        esac
        ;;
    button/lid)
        case "$3" in
            close)
                logger 'LID closed'
                DISPLAY=:0.0 su -c - your_username /usr/bin/slimlock &
                systemctl suspend
                ;;
            open)
                logger 'LID opened'
                ;;
            *)
                logger "ACPI action undefined: $3"
                ;;
    esac
    ;;
    *)
        logger "ACPI group/action undefined: $1 / $2"
        ;;
esac

# vim:set ts=4 sw=4 ft=sh et:

借助 zenity 编写系统操作的小程序

借助 zenity 编写系统操作APP, 支持 睡眠(挂起)/深度睡眠/休眠/关机/重启(在休眠前启用 swap 分区):

#!/bin/sh

function Suspend() {
    slimlock &
    sleep 1
    systemctl suspend
}

HybridSleep() {
    slimlock &
    sleep 1
    /usr/bin/swapon /swapfile && systemctl hybrid-sleep
}

function Hibernate() {
    slimlock &
    /usr/bin/swapon /swapfile && systemctl hibernate
}

function Shutdown() {
    zenity --question --text="确定关机?" && \
        # echo "do shutdown" \
        systemctl poweroff
}

function Reboot() {
    zenity --question --text="确定重启?" && \
        # echo "do reboot" \
        systemctl reboot
}

type=$(zenity --list \
    --timeout="10" \
    --width="200" \
    --height="240" \
    --title="要抛弃我啦?" \
    --column="操作" \
    "Suspend" \
    "HybridSleep" \
    "Hibernate" \
    "Shutdown" \
    "Reboot"
)

ret=$?

if [ $ret == 1 ]
then
    # 点击 "关闭" 或 "取消"
    echo "no choice, exit"
    exit
elif [ $ret == 5 ]
then
    echo "timeout"
    exit
fi

eval $type

参考链接:

posted @ 2017-02-26 01:25 whilst 阅读(...) 评论(...) 编辑 收藏