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"
相关推荐
代码AC不AC1 天前
【Linux】计算机的基石:从冯·诺依曼体系结构到操作系统管理
linux·操作系统·冯诺依曼体系结构
大柏怎么被偷了1 天前
【Linux】进程等待
linux·运维·服务器
偶像你挑的噻1 天前
12-Linux驱动开发- SPI子系统
linux·驱动开发·stm32·嵌入式硬件
松涛和鸣1 天前
16、C 语言高级指针与结构体
linux·c语言·开发语言·数据结构·git·算法
念风1 天前
[lvgl]如何优雅地向lv_port_linux中添加tslib支持
linux
自由的好好干活1 天前
使用Qoder编写ztdaq的C#跨平台示例总结
linux·windows·c#·qoder
赖small强1 天前
【Linux 网络基础】libwebsockets HTTPS 服务端实现机制详解
linux·网络·https·tls·libwebsockets
optimistic_chen1 天前
【Redis 系列】Redis详解
linux·数据库·redis·缓存·xsheel
低客的黑调1 天前
了解JVM 结构和运行机制,从小白编程Java 大佬
java·linux·开发语言
想唱rap1 天前
C++ map和set
linux·运维·服务器·开发语言·c++·算法