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

參考鏈接:


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM