Ubuntu 24.04环境下的挂起转休眠

使用systemd的sleep钩子实现的电源管理脚本,合盖首先是挂起,5分钟检查电源状态如果是电池供电则进行休眠,否则还是挂起。系统需已经支持休眠,可执行systemctl hibernate查看休眠是否已启用。

脚本如下,需放到/usr/lib/systemd/system-sleep下,并有执行权限。

bash 复制代码
#!/bin/bash

LOG_FILE="/tmp/smart-suspend.log"
WAKEUP_TIME_FILE="/var/run/suspend-wakeup-time"
SUSPEND_MODE_FILE="/var/run/suspend-mode"

# 确保日志文件可写入
touch "$LOG_FILE"
chmod 644 "$LOG_FILE"

echo "==========================================" >> "$LOG_FILE"
echo "$(date): systemd-sleep hook: $1 $2" >> "$LOG_FILE"

# 函数:检查是否是RTC唤醒
is_rtc_wakeup() {
    # shell function return 0 is true 1 is false
    # 检查alarm_IRQ状态
    if [ -f "/proc/driver/rtc" ]; then
        RTC_INFO=$(cat /proc/driver/rtc)
        ALARM_IRQ=$(echo "$RTC_INFO" | grep -oP 'alarm_IRQ\s*:\s*\K(yes|no|true|false|[01])')
        
        echo "$(date): Raw alarm_IRQ value: '$ALARM_IRQ'" >> "$LOG_FILE"
        
        case "$ALARM_IRQ" in
            yes|true|1)
                echo "$(date): Detected RTC wakeup (alarm_IRQ positive)" >> "$LOG_FILE"
                return 1
                ;;
            no|false|0)
                echo "$(date): No RTC wakeup (alarm_IRQ negative)" >> "$LOG_FILE"
                return 0
                ;;
            *)
                echo "$(date): Unknown alarm_IRQ format: '$ALARM_IRQ'" >> "$LOG_FILE"
                return 0
                ;;
        esac
    fi

    # 备用检查方法
    if [ -f "/sys/class/rtc/rtc0/device/power/wakeup" ]; then
        WAKEUP_STATUS=$(cat "/sys/class/rtc/rtc0/device/power/wakeup")
        if [ "$WAKEUP_STATUS" = "enabled" ]; then
            echo "$(date): Fallback check: RTC wakeup enabled" >> "$LOG_FILE"
            return 1
        fi
    fi

    return 0
}

case "$1" in
    pre)
        # 挂起之前执行
        if [ "$2" = "suspend" ]; then
            # 检查是否需要设置RTC唤醒
            if [ ! -f "$SUSPEND_MODE_FILE" ]; then
                echo "$(date): First suspend - setting RTC wakeup in 5 minutes" >> "$LOG_FILE"
                echo "rtc_wakeup" > "$SUSPEND_MODE_FILE"
                
                # 设置RTC唤醒(5分钟后)
                WAKEUP_TIME=$(($(date +%s) + 300))
                echo "$WAKEUP_TIME" > "$WAKEUP_TIME_FILE"
                rtcwake -m no -s 300
                echo "$(date): RTC wakeup scheduled at $(date -d @$WAKEUP_TIME)" >> "$LOG_FILE"
            else
                echo "$(date): Continuing suspend without RTC wakeup" >> "$LOG_FILE"
                # 这是继续挂起,不设置RTC唤醒
            fi
        fi
        ;;
    post)
        # 恢复之后执行
        if [ "$2" = "suspend" ]; then
            echo "$(date): Resumed from suspend" >> "$LOG_FILE"
            
            # 等待系统稳定
            sleep 2
            
            # 检查唤醒原因
            IS_RTC_WAKEUP=false
            if is_rtc_wakeup; then
                IS_RTC_WAKEUP=true
                echo "$(date): Confirmed RTC wakeup" >> "$LOG_FILE"
            else
                echo "$(date): Not RTC wakeup (likely user wakeup)" >> "$LOG_FILE"
            fi  
                      
            if [ -f "$SUSPEND_MODE_FILE" ] && [ -f "$WAKEUP_TIME_FILE" ]; then
                SUSPEND_MODE=$(cat "$SUSPEND_MODE_FILE")
                SCHEDULED_TIME=$(cat "$WAKEUP_TIME_FILE")
                CURRENT_TIME=$(date +%s)
                TIME_DIFF=$((CURRENT_TIME - SCHEDULED_TIME))
                
                echo "$(date): Scheduled: $(date -d @$SCHEDULED_TIME), Current: $(date -d @$CURRENT_TIME)" >> "$LOG_FILE"
                echo "$(date): Time difference: $TIME_DIFF seconds" >> "$LOG_FILE"
                echo "$(date): RTC wakeup detected: $IS_RTC_WAKEUP" >> "$LOG_FILE"
                
                # 判断是否是RTC定时唤醒
                if [ "$TIME_DIFF" -ge -30 ] && [ "$TIME_DIFF" -le 30 ] && $IS_RTC_WAKEUP; then
                    echo "$(date): Woke up by RTC timer" >> "$LOG_FILE"
                    
                    # 检查当前电源状态
                    if on_ac_power; then
                        echo "$(date): On AC power - preparing to resume suspend" >> "$LOG_FILE"
                        # 修改挂起模式标记,表示这是继续挂起
                        echo "continue_suspend" > "$SUSPEND_MODE_FILE"
                        # 清除RTC时间文件但保留模式标记
                        rm -f "$WAKEUP_TIME_FILE"
                        # 使用正确的时间格式
                        echo "$(date): Resuming suspend without RTC" >> "$LOG_FILE"
                        echo "systemctl suspend" | at now + 0 min 2>/dev/null
                    else
                        echo "$(date): On battery power - switching to hibernate" >> "$LOG_FILE"
                        # 清除所有状态
                        rm -f "$SUSPEND_MODE_FILE" "$WAKEUP_TIME_FILE"
                        # 转为休眠
                        echo "systemctl hibernate" | at now + 0 min 2>/dev/null
                    fi
                else
                    echo "$(date): Woke up by user (not RTC timer) - cleaning up" >> "$LOG_FILE"
                    # 用户提前唤醒,清除所有状态
                    rm -f "$SUSPEND_MODE_FILE" "$WAKEUP_TIME_FILE"
                    rtcwake -m disable
                fi
            else
                echo "$(date): Normal resume or state inconsistent - cleaning up" >> "$LOG_FILE"
                rm -f "$SUSPEND_MODE_FILE" "$WAKEUP_TIME_FILE"
            fi
        fi
        ;;
esac

echo "$(date): systemd-sleep hook completed" >> "$LOG_FILE"
相关推荐
HLC++11 小时前
Linux的进程间通信
android·linux·服务器
华清远见IT开放实验室12 小时前
实验室建设案例 | 石家庄科技信息职业学院嵌入式实验室——从底层硬件到系统应用,一所应用型高校的嵌入式人才培养这样落地
linux·arm开发·stm32·嵌入式硬件·高校·实验室建设
groundhappy15 小时前
idalib安装和codex ida-mcp配置
linux·开发语言·python
通信小小昕15 小时前
Ubuntu 26.04 中文输入法安装
linux·运维·ubuntu
张小姐的猫16 小时前
【Linux】网络编程 —— HTTP协议(上)
linux·运维·服务器·网络·http·单例模式·策略模式
栩栩云生17 小时前
AI 写代码犯的错,早被写进了错题集
linux·安全·ai编程
imc.1118 小时前
linux基础IO
linux·运维·服务器
BelongPanda20 小时前
Linux Nginx 纯手动 Let‘s Encrypt 泛域名证书配置教程
linux·nginx
酷可达拉斯20 小时前
Linux操作系统-shell编程(0)
linux·运维·服务器·python·云计算
2301_7779983420 小时前
Linux中断机制:操作系统如何高效运行
linux·运维·服务器