当前位置: 首页>>技术教程>>正文


如何从Suspend自动进入Hibernate?

, ,

问题描述

是否有可能让Ubuntu从Suspend进入Hibernate状态,又称”Suspend Sedation”?

例如,我的笔记本电脑设置为在关闭盖子后进入暂停状态。如果那时我不使用它一整天,电池就会变平,因为即使在挂起模式下,硬件仍会消耗少量电量,电池最终会放电。我想要的是能够告诉Ubuntu,即使它被暂停,它仍然需要在几个小时的不活动后进入Hibernate。

Windows可以做到这一点。可以将Ubuntu编程为在计时器上进入待机或休眠,但不能同时进入两者。

更新:

我想我需要更具体一点。我正在寻找的是:当我关闭盖子时,笔记本电脑将进入暂停状态。然后,在pre-determined时间之后(即使电池电量很强)如果我仍然不使用它,它应该进入休眠状态以节省电池电量。

最佳解决思路

在Ubuntu 18.04中它更加轻松。在systemd中可以使用新模式suspend-then-hibernate。要开始使用此功能,您需要使用下一个内容创建一个文件/etc/systemd/sleep.conf:

[Sleep]
HibernateDelaySec=3600

然后你可以通过命令测试它:

sudo systemctl suspend-then-hibernate

(您可以编辑HibernateDelaySec以减少休眠延迟)。如果一切正常,你可以改变Lid Close Action,为此你需要编辑文件/etc/systemd/logind.conf

您需要找到选项HandleLidSwitch =,取消注释并更改为HandleLidSwitch = suspend-then-hibernate。然后,您需要通过下一个命令重新启动logind服务(警告!您将重启用户会话):

sudo systemctl restart systemd-logind.service

就这样!现在你可以使用这个不错的功能。

次佳解决思路

解决方案很简单。首先,在暂停和恢复时,pm-suspend程序在/etc/pm/sleep.d/usr/lib/pm-utils/sleep.d中执行一系列脚本。所以我的解决方案是添加一个执行以下操作的脚本:

  1. 暂停后,记录当前时间并使用rtcwake注册唤醒事件。

  2. 恢复后,根据上面记录的时间检查当前时间。如果已经过了足够的时间,那么我们可能会因为rtc计时器事件而醒来。否则,我们会因用户事件(例如打开笔记本电脑屏幕)而提前醒来。

  3. 如果我们因rtc计时器而醒来,则立即发出”pm-hibernate”命令进入休眠状态。

这是一个执行此操作的脚本。将其命名为0000rtchibernate并将其放在/etc/pm/sleep.d目录中(0000很重要,因此脚本在挂起时执行,最后在恢复时执行)。

#!/bin/bash
# Script name: /etc/pm/sleep.d/0000rtchibernate
# Purpose: Auto hibernates after a period of sleep
# Edit the "autohibernate" variable below to set the number of seconds to sleep.
curtime=$(date +%s)
autohibernate=7200
echo "$curtime $1" >>/tmp/autohibernate.log
if [ "$1" = "suspend" ]
then
    # Suspending.  Record current time, and set a wake up timer.
    echo "$curtime" >/var/run/pm-utils/locks/rtchibernate.lock
    rtcwake -m no -s $autohibernate
fi

if [ "$1" = "resume" ]
then
    # Coming out of sleep
    sustime=$(cat /var/run/pm-utils/locks/rtchibernate.lock)
    rm /var/run/pm-utils/locks/rtchibernate.lock
    # Did we wake up due to the rtc timer above?
    if [ $(($curtime - $sustime)) -ge $autohibernate ]
    then
        # Then hibernate
        rm /var/run/pm-utils/locks/pm-suspend.lock
        /usr/sbin/pm-hibernate
    else
        # Otherwise cancel the rtc timer and wake up normally.
        rtcwake -m no -s 1
    fi
fi

希望这个代码在这个留言板上出现(这是我在这里的第一篇文章)。

编辑顶部的超时值autohibernate=7200,无论您在进入休眠状态之前要睡多少秒。上面的当前值是2小时。请注意,当笔记本电脑执行休眠功能时,笔记本电脑将在此时唤醒几秒钟。

因此,如果您打算将笔记本电脑放在机箱中,请不要暂停,而是暂停休眠。否则你的笔记本电脑可能会过热。如果是在一个紧身滑动的情况下(虽然它只会持续几秒到一分钟)。

在过去的几天里,我一直在使用这种方法,到目前为止它已经成功(并且今天下午让我免于死电)。请享用。

对于使用systemd和更新的Ubuntu版本的其他Linux发行版,如果将脚本放在/usr/lib/systemd/system-sleep而不是/etc/pm/sleep.d中,这仍然有用。另外,用systemctl hibernate替换/usr/sbin/pm-hibernate命令。

第三种解决思路

用简单的话来解释它是如何工作的(这类似于Windows):当电池电量不足以便能够将机器状态保存到交换分区时,机器不会从待机状态唤醒,它会立即将所有内容保存到交换分区在待机状态下,当电池电量耗尽时,它将通过从交换分区加载状态来恢复(就像你休眠时一样)。

AFAIK linux将/应该使用混合备用/休眠而不是”normal”备用,如果它知道它适用于您的硬件。目前也可能因为太多错误或其他原因而被禁用…;)

如果你喜欢试验,也许你可以看看你是否可以用pm-suspend-hybrid获得任何好的结果。

如果以下说你很幸运,那么理论上你的系统支持混合暂停:

pm-is-supported --suspend-hybrid && echo "you're lucky"

第四种思路

可能感兴趣:s2both。它由Ubuntu 10.10中的uswsusp包提供。它挂起到磁盘,但不是关闭系统而是将其放入S3,这是通常与Ubuntu中的”Suspend”选项相关联的电源模式。 pm-suspend-hybrid是另一种声称可以做同样事情的工具。

要使盖子上的自动关闭,请查看以下指南,该指南允许您在捕获盖子事件时运行任意脚本:

http://ubuntuforums.org/showthread.php?t=1076486

如果碰巧有ThinkPad,tpctl的联机帮助页引用了一个参数--pm-sedation-hibernate-from-suspend-timer,它似乎提供了您正在寻找的功能。我会提醒您不要在non-ThinkPad硬件上尝试此操作。

作为参考,我查看了hibernate.conf的联机帮助页;它似乎没有任何相关的选择,但可能值得二读。

第五种思路

Ubuntu 16.04 – 在pre-determined时间之后从暂停/睡眠进入休眠状态

似乎在Ubuntu 16.04上的东西有点不同,所以我采取的步骤是:

  1. 确保hibernate在运行时按预期工作

    systemctl hibernate
    
  2. 复制原始suspend.target文件:

    sudo cp /lib/systemd/system/suspend.target /etc/systemd/system/suspend.target
    

    然后编辑文件/etc/systemd/system/suspend.target并添加以下行:

    Requires=delayed-hibernation.service
    

    到该文件的[Unit]部分。

  3. 使用以下内容创建文件/etc/systemd/system/delayed-hibernation.service


[Unit]
Description=Delayed hibernation trigger
Before=suspend.target
Conflicts=hibernate.target hybrid-suspend.target
StopWhenUnneeded=true

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/delayed-hibernation.sh pre suspend
ExecStop=/usr/local/bin/delayed-hibernation.sh post suspend

[Install]
WantedBy=sleep.target
  1. 使用以下内容为脚本创建配置文件 /etc/delayed-hibernation.conf


# Configuration file for 'delayed-hibernation.sh' script

# Specify the time in seconds to spend in sleep mode before the computer hibernates
TIMEOUT=1200  #in seconds, gives 20 minutes
  1. 创建实际完成艰苦工作的脚本。使用以下内容创建文件/usr/local/bin/delayed-hibernation.sh


#!/bin/bash
# Script name: delayed-hibernation.sh
# Purpose: Auto hibernates after a period of sleep
# Edit the `TIMEOUT` variable in the `$hibernation_conf` file to set the number of seconds to sleep.

hibernation_lock='/var/run/delayed-hibernation.lock'
hibernation_fail='/var/run/delayed-hibernation.fail'
hibernation_conf='/etc/delayed-hibernation.conf'

# Checking the configuration file
if [ ! -f $hibernation_conf ]; then
    echo "Missing configuration file ('$hibernation_conf'), aborting."
    exit 1
fi
hibernation_timeout=$(grep "^[^#]" $hibernation_conf | grep "TIMEOUT=" | awk -F'=' '{ print $2 }' | awk -F'#' '{print $1}' | tr -d '[[ \t]]')
if [ "$hibernation_timeout" = "" ]; then
    echo "Missing 'TIMEOUT' parameter from configuration file ('$hibernation_conf'), aborting."
    exit 1
elif [[ ! "$hibernation_timeout" =~ ^[0-9]+$ ]]; then
    echo "Bad 'TIMEOUT' parameter ('$hibernation_timeout') in configuration file ('$hibernation_conf'), expected number of seconds, aborting."
    exit 1
fi

# Processing given parameters
if [ "$2" = "suspend" ]; then
    curtime=$(date +%s)
    if [ "$1" = "pre" ]; then
        if [ -f $hibernation_fail ]; then
            echo "Failed hibernation detected, skipping setting RTC wakeup timer."
        else
            echo "Suspend detected. Recording time, set RTC timer"
            echo "$curtime" > $hibernation_lock
            rtcwake -m no -s $hibernation_timeout
        fi
    elif [ "$1" = "post" ]; then
        if [ -f $hibernation_fail ]; then
            rm $hibernation_fail
        fi
        if [ -f $hibernation_lock ]; then
            sustime=$(cat $hibernation_lock)
            rm $hibernation_lock
            if [ $(($curtime - $sustime)) -ge $hibernation_timeout ]; then
                echo "Automatic resume from suspend detected. Hibernating..."
                systemctl hibernate
                if [ $? -ne 0 ]; then
                    echo "Automatic hibernation failed. Trying to suspend instead."
                    touch $hibernation_fail
                    systemctl suspend
                    if [ $? -ne 0 ]; then
                        echo "Automatic hibernation and suspend failover failed. Nothing else to try."
                    fi
                fi
            else
                echo "Manual resume from suspend detected. Clearing RTC timer"
                rtcwake -m disable
            fi
        else
            echo "File '$hibernation_lock' was not found, nothing to do"
        fi
    else
        echo "Unrecognised first parameter: '$1', expected 'pre' or 'post'"
    fi
else
    echo "This script is intended to be run by systemctl delayed-hibernation.service (expected second parameter: 'suspend')"
fi
  1. 使脚本可执行:


chmod 755 /usr/local/bin/delayed-hibernation.sh

在我根据这个帖子中的其他回复编写这个脚本之前我花了很多东西,我在互联网上找到的东西,比如https://bbs.archlinux.org/viewtopic.php?pid=1554259

我的脚本版本尝试处理许多问题,例如如果hibernate没有成功则再次进入暂停,但在pre-determined时间之后不要再次唤醒。

  1. 我假设的最后一步是执行

    sudo systemctl daemon-reload
    sudo systemctl enable delayed-hibernation.service 
    

    确保使用新的服务/配置。

要检查服务日志,您可以使用:

sudo systemctl status delayed-hibernation.service

或者有关服务使用的完整日志:

sudo journalctl -u delayed-hibernation.service

我从正在运行的服务获得的正常日志是:


mile@mile-ThinkPad:~$ sudo systemctl status delayed-hibernation.service 
● delayed-hibernation.service - Delayed hibernation trigger
   Loaded: loaded (/etc/systemd/system/delayed-hibernation.service; enabled; vendor preset: enabled)
   Active: inactive (dead)

Jun 09 20:35:42 mile-ThinkPad systemd[1]: Starting Delayed hibernation trigger...
Jun 09 20:35:42 mile-ThinkPad delayed-hibernation.sh[2933]: Suspend detected. Recording time, set RTC timer
Jun 09 20:35:42 mile-ThinkPad delayed-hibernation.sh[2933]: rtcwake: assuming RTC uses UTC ...
Jun 09 20:35:42 mile-ThinkPad delayed-hibernation.sh[2933]: rtcwake: wakeup using /dev/rtc0 at Thu Jun  9 18:55:43 2016
Jun 09 20:55:44 mile-ThinkPad systemd[1]: Started Delayed hibernation trigger.
Jun 09 20:55:44 mile-ThinkPad systemd[1]: delayed-hibernation.service: Unit not needed anymore. Stopping.
Jun 09 20:55:44 mile-ThinkPad systemd[1]: Stopping Delayed hibernation trigger...
Jun 09 20:55:44 mile-ThinkPad delayed-hibernation.sh[3093]: Automatic resume from suspend detected. Hibernating...
Jun 09 20:55:44 mile-ThinkPad systemd[1]: Stopped Delayed hibernation trigger.
mile@mile-ThinkPad:~$ 

所以这就是它,我希望它真的能帮助别人,因为我花了几天时间试图找出配置和脚本版本的正确组合,以使这个方便的功能工作。

第六种思路

为了防止在pm-hibernate期间出现问题,我宁愿让计算机暂停而不是让它运行。所以你可以使用:

   ...
/usr/sbin/pm-hibernate || /usr/sbin/pm-suspend
   ...

第七种思路

这是Derek Pressnall’s answer的更新版本,它与systemd一起使用并包含Eliah Kagan’s suggestion,只需将其放入/usr/lib/systemd/system-sleep/delayed_hibernation.sh并使其可执行:

#!/bin/bash

hibernation_timeout=1800  #30 minutes

if [ "$2" = "suspend" ]; then
    curtime=$(date +%s)
    if [ "$1" = "pre" ]; then
        echo -e "[($curtime) $@]\nExecuting pre-suspend hook..." >> /tmp/delayed_hibernation.log
        echo "$curtime" > /var/run/delayed_hibernation.lock
        rtcwake -m no -s $hibernation_timeout
    elif [ "$1" = "post" ]; then
        echo -e "[($curtime) $@]\nExecuting post-suspend hook..." >> /tmp/delayed_hibernation.log
        sustime=$(cat /var/run/delayed_hibernation.lock)
        if [ $(($curtime - $sustime)) -ge $hibernation_timeout ]; then
            echo -e "Automatic resume detected, hibernating.\n" >> /tmp/delayed_hibernation.log
            systemctl hibernate || systemctl suspend
        else
            echo -e "Manual resume detected, clearing RTC alarm.\n" >> /tmp/delayed_hibernation.log
            rtcwake -m no -s 1
        fi
        rm /var/run/delayed_hibernation.lock
    fi
fi

第八种思路

这是我的食谱(在两个笔记本Ubuntu 16.04上测试过):

把这个脚本放在你喜欢的地方(我把它放到root,/syspend.sh)并使它可执行(chmod +x /suspend.sh)

TIMELOG=/tmp/autohibernate.log
ALARM=$(tail -n 1 $TIMELOG)
SLEEPTIME=5000 #edit this line to change timer, e.g. 2 hours "$((2*60*60))"
if [[ $1 == "resume" ]]
then
    if [[ $(date +%s) -ge $(( $ALARM + $SLEEPTIME )) ]]
    then
        echo "hibernate triggered $(date +%H:%M:%S)">>$TIMELOG
        systemctl hibernate 2>> $TIMELOG
    else
        echo "normal wakeup $(date +%H:%M:%S)">>$TIMELOG
    fi
elif [[ $1 == "suspend" ]]
then
    echo "$(date +%s)" >> $TIMELOG
    rtcwake -m no -s $SLEEPTIME
fi

然后创建systemd目标:# touch /etc/systemd/system/suspend-to-sleep.target粘贴此内容:

#/etc/systemd/system/suspend-to-hibernate.service
[Unit]
Description=Delayed hibernation trigger
Before=suspend.target
Conflicts=hibernate.target hybrid-suspend.target
StopWhenUnneeded=true

[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/bin/bash /suspend.sh suspend
ExecStop=/bin/bash /suspend.sh wakeup

[Install]
WantedBy=sleep.target
RequiredBy=suspend.target

然后启用它# systemctl enable suspend-to-sleep.target

我在笔记本电脑上遇到了一个问题:关闭盖子没有触发这个目标。这是由于xfce4-power-manager。有两种方法可以解决此问题。第一个是编辑/etc/systemd/logind.conf文件并用HandleLidSwitch=suspend替换HandleLidSwitch=ignore。但它将是系统范围的,所以我只是将symlink添加到我的脚本# ln -s /suspend.sh /etc/pm/sleep.d/0000rtchibernate

参考资料

本文由Ubuntu问答整理, 博文地址: https://ubuntuqa.com/article/1858.html,未经允许,请勿转载。