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"
相关推荐
Yana.nice41 分钟前
openssl将证书从p7b转换为crt格式
java·linux
AI逐月1 小时前
tmux 常用命令总结:从入门到稳定使用的一篇实战博客
linux·服务器·ssh·php
小白跃升坊1 小时前
基于1Panel的AI运维
linux·运维·人工智能·ai大模型·教学·ai agent
跃渊Yuey2 小时前
【Linux】线程同步与互斥
linux·笔记
舰长1152 小时前
linux 实现文件共享的实现方式比较
linux·服务器·网络
zmjjdank1ng2 小时前
Linux 输出重定向
linux·运维
路由侠内网穿透.2 小时前
本地部署智能家居集成解决方案 ESPHome 并实现外部访问( Linux 版本)
linux·运维·服务器·网络协议·智能家居
VekiSon2 小时前
Linux内核驱动——基础概念与开发环境搭建
linux·运维·服务器·c语言·arm开发
zl_dfq3 小时前
Linux 之 【进程信号】(signal、kill、raise、abort、alarm、Core Dump核心转储机制)
linux
Ankie Wan3 小时前
cgroup(Control Group)是 Linux 内核提供的一种机制,用来“控制、限制、隔离、统计”进程对系统资源的使用。
linux·容器·cgroup·lxc