Linux 休眠唤醒机制详解(上)

1. 概述

1.1 什么是休眠和唤醒?

休眠(Suspend)的定义

休眠是 Linux 系统的一种电源管理功能,允许系统进入低功耗状态,同时保持系统状态,以便快速恢复。在休眠状态下,系统的大部分硬件被关闭或进入低功耗模式,只有必要的硬件保持运行以支持唤醒。

唤醒(Resume)的定义

唤醒是从休眠状态恢复到正常工作状态的过程。当系统接收到唤醒事件(如按键、网络数据包、定时器等)时,系统会从休眠状态恢复,恢复到休眠前的状态。

休眠和唤醒的目的

  1. 节省功耗:在系统不使用时降低功耗
  2. 快速恢复:快速恢复到工作状态
  3. 保持状态:保持系统状态,不需要重新启动

1.2 Linux 休眠状态

ACPI 休眠状态

Linux 支持 ACPI(Advanced Configuration and Power Interface)定义的休眠状态:

状态 名称 功耗 恢复时间 状态保存位置
S0 Working 正常 - -
S1 Standby 很快 RAM
S2 Standby 更低 很快 RAM
S3 Suspend to RAM (STR) 很低 RAM
S4 Suspend to Disk (STD/Hibernate) 极低 磁盘
S5 Shutdown 很慢 -

Linux 中的休眠状态

Linux 主要支持以下休眠状态:

  1. mem (Suspend to RAM / S3)

    • 系统状态保存在 RAM 中
    • 大部分硬件关闭
    • 恢复时间快(几秒)
    • 功耗低
  2. disk (Suspend to Disk / Hibernate / S4)

    • 系统状态保存到磁盘
    • 所有硬件关闭
    • 恢复时间慢(几十秒)
    • 功耗极低(几乎为零)
  3. standby (S1)

    • 系统状态保存在 RAM 中
    • 部分硬件关闭
    • 恢复时间很快
    • 功耗较低
  4. freeze (Suspend to Idle)

    • 系统进入空闲状态
    • 所有用户空间进程冻结
    • CPU 进入空闲状态
    • 功耗最低(但保持运行)

1.3 休眠唤醒的流程

休眠流程

markdown 复制代码
1. 用户触发休眠(echo mem > /sys/power/state)
   ↓
2. 冻结用户空间进程
   ↓
3. 冻结内核线程
   ↓
4. 调用设备驱动的 suspend 回调
   ↓
5. 保存系统状态
   ↓
6. 进入休眠状态

唤醒流程

markdown 复制代码
1. 唤醒事件发生(按键、网络数据包等)
   ↓
2. 硬件中断触发
   ↓
3. 从休眠状态恢复
   ↓
4. 恢复系统状态
   ↓
5. 调用设备驱动的 resume 回调
   ↓
6. 解冻内核线程
   ↓
7. 解冻用户空间进程
   ↓
8. 系统恢复正常运行

2. 休眠流程详解

2.1 用户空间触发休眠

通过 sysfs 触发休眠

bash 复制代码
# 进入 Suspend to RAM
echo mem > /sys/power/state

# 进入 Suspend to Disk (Hibernate)
echo disk > /sys/power/state

# 进入 Suspend to Idle
echo freeze > /sys/power/state

sysfs 接口的实现

c 复制代码
// kernel/power/main.c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
                           const char *buf, size_t n)
{
    suspend_state_t state;
    int error;
    
    // 1. 解析状态字符串
    error = pm_autosleep_lock();
    if (error)
        return error;
    
    if (pm_autosleep_state() > PM_SUSPEND_ON) {
        error = -EBUSY;
        goto out;
    }
    
    // 2. 转换状态字符串到状态值
    state = decode_state(buf, n);
    if (state < PM_SUSPEND_MAX) {
        if (state == PM_SUSPEND_MEM)
            state = mem_sleep_current;
        
        // 3. 调用休眠函数
        error = pm_suspend(state);
    } else if (state == PM_SUSPEND_MAX) {
        error = hibernate();
    } else {
        error = -EINVAL;
    }
    
out:
    pm_autosleep_unlock();
    return error ? error : n;
}

2.2 进程冻结(Freeze)

冻结用户空间进程

c 复制代码
// kernel/power/process.c
int freeze_processes(void)
{
    int error;
    
    // 1. 设置系统冻结标志
    error = __usermodehelper_disable(UMH_FREEZING);
    if (error)
        return error;
    
    // 2. 冻结用户空间进程
    error = try_to_freeze_tasks(true);
    if (!error) {
        __usermodehelper_set_disable_depth(UMH_DISABLED);
        pr_info("Freezing user space processes ... ");
        error = try_to_freeze_tasks(false);
        if (error)
            goto exit;
        pr_cont("done.\n");
    }
    
exit:
    __usermodehelper_thaw();
    return error;
}

try_to_freeze_tasks() 实现

c 复制代码
// kernel/power/process.c
static int try_to_freeze_tasks(bool user_only)
{
    struct task_struct *g, *p;
    unsigned long end_time;
    unsigned int todo;
    bool wq_busy = false;
    struct timeval start, end;
    u64 elapsed_msecs64;
    unsigned int elapsed_msecs;
    bool wakeup = false;
    int sleep_usecs = USEC_PER_MSEC;
    
    end_time = jiffies + msecs_to_jiffies(freeze_timeout_msecs);
    
    do {
        todo = 0;
        wq_busy = false;
        
        // 遍历所有进程
        read_lock(&tasklist_lock);
        for_each_process_thread(g, p) {
            if (p == current || !freeze_task(p))
                continue;
            
            if (!freezer_should_skip(p))
                todo++;
        }
        read_unlock(&tasklist_lock);
        
        // 检查工作队列
        if (todo && has_wake_lock(WAKE_LOCK_SUSPEND)) {
            wq_busy = true;
            todo = 0;
        }
        
        if (!todo || time_after(jiffies, end_time))
            break;
        
        // 等待一段时间后重试
        usleep_range(sleep_usecs, sleep_usecs + sleep_usecs / 2);
        sleep_usecs *= 2;
    } while (todo);
    
    return todo ? -EBUSY : 0;
}

freeze_task() 实现

c 复制代码
// kernel/freezer.c
bool freeze_task(struct task_struct *p)
{
    unsigned long flags;
    
    spin_lock_irqsave(&freezer_lock, flags);
    
    // 1. 检查是否可以冻结
    if (!freezing(p) || frozen(p) || __refrigerator(p)) {
        spin_unlock_irqrestore(&freezer_lock, flags);
        return false;
    }
    
    // 2. 设置冻结标志
    set_freeze_flag(p);
    
    // 3. 发送冻结信号
    signal_wake_up(p, false);
    
    spin_unlock_irqrestore(&freezer_lock, flags);
    return true;
}

2.3 设备驱动的 suspend 回调

设备驱动的 suspend 实现

c 复制代码
// drivers/base/power/main.c
int dpm_suspend(pm_message_t state)
{
    struct device *dev;
    int error = 0;
    
    // 1. 按顺序调用所有设备的 suspend 回调
    list_for_each_entry_reverse(dev, &dpm_list, power.entry) {
        error = device_suspend(dev, state);
        if (error) {
            pm_dev_err(dev, state, "", error);
            break;
        }
    }
    
    return error;
}

static int device_suspend(struct device *dev, pm_message_t state)
{
    pm_callback_t callback = NULL;
    const char *info = NULL;
    int error = 0;
    
    // 1. 调用驱动的 suspend 回调
    if (dev->driver && dev->driver->pm && dev->driver->pm->suspend) {
        info = "legacy PM ";
        callback = pm_late_early_op(dev->driver->pm->suspend, state);
    } else if (dev->type && dev->type->pm) {
        info = "legacy type PM ";
        callback = pm_late_early_op(dev->type->pm->suspend, state);
    } else if (dev->class && dev->class->pm) {
        info = "legacy class PM ";
        callback = pm_late_early_op(dev->class->pm->suspend, state);
    } else if (dev->bus && dev->bus->pm) {
        info = "legacy bus PM ";
        callback = pm_late_early_op(dev->bus->pm->suspend, state);
    }
    
    if (callback)
        error = callback(dev);
    
    return error;
}

示例:网卡驱动的 suspend

c 复制代码
// drivers/net/ethernet/example.c
static int example_suspend(struct device *dev)
{
    struct net_device *netdev = dev_get_drvdata(dev);
    struct example_private *priv = netdev_priv(netdev);
    
    // 1. 停止网络接口
    netif_device_detach(netdev);
    
    // 2. 停止接收和发送
    napi_disable(&priv->napi);
    
    // 3. 关闭网卡
    example_stop(netdev);
    
    // 4. 保存寄存器状态
    priv->saved_regs = ioread32(priv->base + REG_CONFIG);
    
    // 5. 进入低功耗模式
    iowrite32(priv->saved_regs | POWER_DOWN, priv->base + REG_CONFIG);
    
    return 0;
}

2.4 进入休眠状态

Suspend to RAM 的实现

c 复制代码
// kernel/power/suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
    int error;
    
    // 1. 禁用中断
    local_irq_disable();
    
    // 2. 刷新缓存
    syscore_suspend();
    
    // 3. 调用平台特定的 suspend 函数
    error = suspend_ops->enter(state);
    
    // 4. 如果返回,说明被唤醒
    syscore_resume();
    
    local_irq_enable();
    
    return error;
}

平台特定的 suspend 实现

c 复制代码
// arch/arm64/kernel/suspend.c
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
    struct sleep_stack_data state;
    
    // 1. 保存 CPU 状态
    __cpu_suspend_enter(&state);
    
    // 2. 调用平台特定的 suspend 函数
    ret = fn(arg);
    
    // 3. 如果返回,恢复 CPU 状态
    __cpu_suspend_exit(&state);
    
    return ret;
}

3. 唤醒流程详解

3.1 唤醒事件

唤醒源(Wakeup Source)

唤醒源是可以唤醒系统的硬件或软件事件:

  1. 硬件唤醒源

    • 按键(键盘、电源键)
    • 网络数据包(Wake-on-LAN)
    • USB 设备插入
    • RTC 定时器
    • 传感器事件
  2. 软件唤醒源

    • 定时器到期
    • 工作队列任务
    • 内核线程

唤醒源的注册

c 复制代码
// drivers/base/power/wakeup.c
struct wakeup_source *wakeup_source_register(struct device *dev,
                                              const char *name)
{
    struct wakeup_source *ws;
    
    // 1. 分配唤醒源结构
    ws = kzalloc(sizeof(*ws), GFP_KERNEL);
    if (!ws)
        return NULL;
    
    // 2. 初始化唤醒源
    wakeup_source_init(ws, name);
    
    // 3. 添加到唤醒源列表
    list_add_rcu(&ws->entry, &wakeup_sources);
    
    return ws;
}

3.2 唤醒中断处理

唤醒中断的处理

c 复制代码
// 示例:按键唤醒中断处理
static irqreturn_t power_button_irq(int irq, void *dev_id)
{
    // 1. 标记唤醒事件
    pm_wakeup_event(dev, 0);
    
    // 2. 唤醒系统
    pm_system_wakeup();
    
    return IRQ_HANDLED;
}

pm_wakeup_event() 实现

c 复制代码
// drivers/base/power/wakeup.c
void pm_wakeup_event(struct device *dev, unsigned int msec)
{
    unsigned long flags;
    
    if (!dev)
        return;
    
    spin_lock_irqsave(&dev->power.lock, flags);
    
    // 1. 更新唤醒源时间
    __pm_wakeup_event(dev->power.wakeup, msec);
    
    spin_unlock_irqrestore(&dev->power.lock, flags);
}

3.3 从休眠状态恢复

平台特定的 resume 实现

c 复制代码
// arch/arm64/kernel/suspend.c
void cpu_resume(void)
{
    // 1. 恢复 CPU 状态
    cpu_suspend_exit();
    
    // 2. 恢复 MMU
    cpu_do_resume(NULL);
    
    // 3. 恢复中断
    local_irq_enable();
}

设备驱动的 resume 回调

c 复制代码
// drivers/base/power/main.c
int dpm_resume(pm_message_t state)
{
    struct device *dev;
    int error = 0;
    
    // 1. 按顺序调用所有设备的 resume 回调
    list_for_each_entry(dev, &dpm_list, power.entry) {
        error = device_resume(dev, state, false);
        if (error) {
            pm_dev_err(dev, state, " async", error);
            break;
        }
    }
    
    return error;
}

示例:网卡驱动的 resume

c 复制代码
// drivers/net/ethernet/example.c
static int example_resume(struct device *dev)
{
    struct net_device *netdev = dev_get_drvdata(dev);
    struct example_private *priv = netdev_priv(netdev);
    
    // 1. 退出低功耗模式
    iowrite32(priv->saved_regs, priv->base + REG_CONFIG);
    
    // 2. 恢复寄存器状态
    iowrite32(priv->saved_regs, priv->base + REG_CONFIG);
    
    // 3. 重新初始化网卡
    example_init(netdev);
    
    // 4. 启动接收和发送
    napi_enable(&priv->napi);
    
    // 5. 恢复网络接口
    netif_device_attach(netdev);
    
    return 0;
}

3.4 解冻进程

解冻用户空间进程

c 复制代码
// kernel/power/process.c
void thaw_processes(void)
{
    struct task_struct *g, *p;
    
    pr_info("Restarting tasks ... ");
    
    // 1. 清除冻结标志
    __usermodehelper_set_disable_depth(UMH_FREEZING);
    
    // 2. 解冻所有进程
    read_lock(&tasklist_lock);
    for_each_process_thread(g, p) {
        __thaw_task(p);
    }
    read_unlock(&tasklist_lock);
    
    // 3. 等待所有进程解冻
    schedule();
    
    pr_cont("done.\n");
}

__thaw_task() 实现

c 复制代码
// kernel/freezer.c
void __thaw_task(struct task_struct *p)
{
    unsigned long flags;
    
    spin_lock_irqsave(&freezer_lock, flags);
    
    // 1. 清除冻结标志
    if (frozen(p)) {
        clear_frozen(p);
    } else {
        WARN_ON(!frozen(p));
    }
    
    // 2. 唤醒进程
    wake_up_process(p);
    
    spin_unlock_irqrestore(&freezer_lock, flags);
}

4. 底层实现原理

4.1 CPU 状态保存和恢复

CPU 寄存器保存

c 复制代码
// arch/arm64/kernel/suspend.c
struct sleep_stack_data {
    struct cpu_suspend_ctx    cpu_context;
    unsigned long              stack[THREAD_SIZE / sizeof(long)];
};

struct cpu_suspend_ctx {
    u64    tpidr_el0;
    u64    tpidrro_el0;
    u64    contextidr_el1;
    u64    sp_el0;
    u64    sp_el1;
    u64    elr_el1;
    u64    spsr_el1;
    u64    x19;
    u64    x20;
    u64    x21;
    u64    x22;
    u64    x23;
    u64    x24;
    u64    x25;
    u64    x26;
    u64    x27;
    u64    x28;
    u64    fp;
    u64    lr;
};

static void notrace __cpu_suspend_enter(struct sleep_stack_data *state)
{
    // 保存 CPU 寄存器
    state->cpu_context = *cpu_suspend_ctx;
}

CPU 寄存器恢复

c 复制代码
// arch/arm64/kernel/suspend.c
void notrace __cpu_suspend_exit(struct sleep_stack_data *state)
{
    // 恢复 CPU 寄存器
    *cpu_suspend_ctx = state->cpu_context;
}

4.2 内存状态保存

Suspend to RAM 的内存状态

在 Suspend to RAM 中,内存状态保存在 RAM 中,不需要特殊处理。系统只需要:

  1. 保持 RAM 供电
  2. 刷新缓存到内存
  3. 进入低功耗模式

Suspend to Disk 的内存状态保存

c 复制代码
// kernel/power/swap.c
int swsusp_write(unsigned int flags)
{
    struct snapshot_handle handle;
    struct swap_map_handle swap;
    struct swsusp_info *header;
    int error;
    
    // 1. 分配交换页面
    error = get_swap_writer(&swap);
    if (error)
        return error;
    
    // 2. 写入头部信息
    header = (struct swsusp_info *)get_image_page(GFP_ATOMIC, PG_ANY);
    if (!header) {
        error = -ENOMEM;
        goto out_finish;
    }
    
    // 3. 保存内存页面
    error = save_image(&swap, &handle, nr_pages);
    if (error)
        goto out_finish;
    
    // 4. 写入结束标记
    error = flush_swap_writer(&swap);
    
out_finish:
    put_swap_writer(&swap);
    return error;
}

4.3 中断和定时器处理

中断的禁用和恢复

c 复制代码
// kernel/power/suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
    // 1. 禁用中断
    local_irq_disable();
    
    // 2. 刷新中断处理
    syscore_suspend();
    
    // 3. 进入休眠
    error = suspend_ops->enter(state);
    
    // 4. 恢复中断处理
    syscore_resume();
    
    // 5. 恢复中断
    local_irq_enable();
    
    return error;
}

定时器的处理

c 复制代码
// kernel/time/tick-common.c
void tick_suspend(void)
{
    struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
    unsigned long flags;
    
    local_irq_save(flags);
    
    // 1. 停止定时器
    clockevents_shutdown(td->evtdev);
    
    local_irq_restore(flags);
}

void tick_resume(void)
{
    struct tick_device *td = this_cpu_ptr(&tick_cpu_device);
    unsigned long flags;
    int broadcast = tick_resume_broadcast();
    
    local_irq_save(flags);
    
    // 1. 恢复定时器
    clockevents_tick_resume(td->evtdev);
    
    local_irq_restore(flags);
}

5. 设备驱动支持

5.1 PM 操作结构

struct dev_pm_ops

c 复制代码
// include/linux/pm.h
struct dev_pm_ops {
    int (*prepare)(struct device *dev);
    void (*complete)(struct device *dev);
    int (*suspend)(struct device *dev);
    int (*resume)(struct device *dev);
    int (*freeze)(struct device *dev);
    int (*thaw)(struct device *dev);
    int (*poweroff)(struct device *dev);
    int (*restore)(struct device *dev);
    int (*suspend_late)(struct device *dev);
    int (*resume_early)(struct device *dev);
    int (*freeze_late)(struct device *dev);
    int (*thaw_early)(struct device *dev);
    int (*poweroff_late)(struct device *dev);
    int (*restore_early)(struct device *dev);
    int (*suspend_noirq)(struct device *dev);
    int (*resume_noirq)(struct device *dev);
    int (*freeze_noirq)(struct device *dev);
    int (*thaw_noirq)(struct device *dev);
    int (*poweroff_noirq)(struct device *dev);
    int (*restore_noirq)(struct device *dev);
    int (*runtime_suspend)(struct device *dev);
    int (*runtime_resume)(struct device *dev);
    int (*runtime_idle)(struct device *dev);
};

PM 回调的执行顺序

perl 复制代码
Suspend:
1. prepare()
2. suspend()
3. suspend_late()
4. suspend_noirq()

Resume:
1. resume_noirq()
2. resume_early()
3. resume()
4. complete()

5.2 唤醒源管理

唤醒源的启用和禁用

c 复制代码
// drivers/base/power/wakeup.c
void device_set_wakeup_capable(struct device *dev, bool capable)
{
    if (capable) {
        if (!dev->power.can_wakeup) {
            dev->power.can_wakeup = true;
            device_wakeup_attach(dev);
        }
    } else {
        if (dev->power.can_wakeup) {
            device_wakeup_detach(dev);
            dev->power.can_wakeup = false;
        }
    }
}

void device_set_wakeup_enable(struct device *dev, bool enable)
{
    if (enable) {
        if (dev->power.can_wakeup && !dev->power.wakeup) {
            dev->power.wakeup = wakeup_source_register(dev, dev_name(dev));
        }
    } else {
        if (dev->power.wakeup) {
            wakeup_source_unregister(dev->power.wakeup);
            dev->power.wakeup = NULL;
        }
    }
}

6. 使用方式

6.1 通过 sysfs 控制休眠

基本用法

bash 复制代码
# 查看支持的休眠状态
cat /sys/power/state

# 进入 Suspend to RAM
echo mem > /sys/power/state

# 进入 Suspend to Disk
echo disk > /sys/power/state

# 进入 Suspend to Idle
echo freeze > /sys/power/state

通过 systemd 控制

bash 复制代码
# 进入休眠
systemctl suspend

# 进入休眠到磁盘
systemctl hibernate

# 进入混合休眠(先休眠到磁盘,然后进入 RAM)
systemctl hybrid-sleep

6.2 通过程序控制休眠

C 语言示例

c 复制代码
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>
#include <string.h>

int suspend_to_ram(void)
{
    int fd;
    const char *state = "mem";
    
    // 打开 sysfs 接口
    fd = open("/sys/power/state", O_WRONLY);
    if (fd < 0) {
        perror("open");
        return -1;
    }
    
    // 写入状态
    if (write(fd, state, strlen(state)) < 0) {
        perror("write");
        close(fd);
        return -1;
    }
    
    close(fd);
    return 0;
}

Python 示例

python 复制代码
#!/usr/bin/env python
import os

def suspend_to_ram():
    """进入 Suspend to RAM"""
    with open('/sys/power/state', 'w') as f:
        f.write('mem')

if __name__ == '__main__':
    suspend_to_ram()

6.3 配置休眠参数

配置唤醒源

bash 复制代码
# 查看唤醒源
cat /sys/power/wake_lock
cat /sys/power/wake_unlock

# 启用/禁用唤醒源
echo "PowerButton" > /sys/power/wake_lock
echo "PowerButton" > /sys/power/wake_unlock

配置休眠超时

bash 复制代码
# 设置自动休眠超时(秒)
echo 300 > /sys/power/autosleep

7. 调试和故障排查

7.1 查看休眠状态

查看支持的休眠状态

bash 复制代码
# 查看支持的休眠状态
cat /sys/power/state

# 查看当前休眠状态
cat /sys/power/mem_sleep

查看唤醒源

bash 复制代码
# 查看所有唤醒源
cat /sys/kernel/debug/wakeup_sources

# 查看特定设备的唤醒源
cat /sys/devices/.../power/wakeup

7.2 调试休眠问题

启用调试日志

bash 复制代码
# 启用 PM 调试
echo 1 > /sys/power/pm_debug_messages

# 查看内核日志
dmesg | grep -i suspend
dmesg | grep -i resume

常见问题

  1. 设备无法休眠

    • 检查是否有阻止休眠的唤醒源
    • 检查设备驱动的 suspend 回调是否实现
    • 查看内核日志中的错误信息
  2. 无法唤醒

    • 检查唤醒源是否启用
    • 检查硬件是否支持唤醒
    • 查看内核日志中的错误信息
  3. 唤醒后系统异常

    • 检查设备驱动的 resume 回调是否正确实现
    • 检查是否有设备状态未正确恢复

8. 休眠唤醒的完整源码解析

8.1 pm_suspend() 完整流程

pm_suspend() 函数

c 复制代码
// kernel/power/suspend.c
int pm_suspend(suspend_state_t state)
{
    int error;
    
    if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
        return -EINVAL;
    
    pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
    
    // 1. 同步文件系统
    error = enter_state(state);
    
    if (error) {
        suspend_stats.fail++;
        dpm_save_failed_errno(error);
    } else {
        suspend_stats.success++;
    }
    
    pr_info("suspend exit\n");
    return error;
}

enter_state() 实现

c 复制代码
// kernel/power/suspend.c
static int enter_state(suspend_state_t state)
{
    int error;
    
    // 1. 检查平台是否支持该状态
    if (!valid_state(state))
        return -ENODEV;
    
    // 2. 检查是否有阻止休眠的唤醒源
    if (!mutex_trylock(&pm_mutex))
        return -EBUSY;
    
    // 3. 同步文件系统
    if (state == PM_SUSPEND_FREEZE) {
        freeze_enter();
    } else {
        error = sync_filesystems(false);
        if (error)
            goto Unlock;
    }
    
    // 4. 冻结进程
    pm_prepare_console();
    suspend_console();
    pm_restrict_gfp_mask();
    error = pm_suspend_freeze(state);
    if (error)
        goto Finish;
    
    // 5. 设备 suspend
    error = pm_suspend_devices_and_enter(state);
    if (error)
        goto Thaw;
    
    // 6. 恢复
    pm_restore_gfp_mask();
    thaw_console();
    pm_restore_console();
    pm_notifier_call_chain(PM_POST_SUSPEND);
    complete_all(&pm_suspend_completion);
    
    return 0;
    
Thaw:
    pm_suspend_thaw();
Finish:
    pm_restore_gfp_mask();
    thaw_console();
    pm_restore_console();
Unlock:
    mutex_unlock(&pm_mutex);
    return error;
}

pm_suspend_devices_and_enter() 实现

c 复制代码
// kernel/power/suspend.c
int pm_suspend_devices_and_enter(suspend_state_t state)
{
    int error;
    bool wakeup = false;
    
    // 1. 准备设备
    error = dpm_suspend_start(PMSG_SUSPEND);
    if (error) {
        log_suspend_abort_reason("Device suspend failed");
        goto Out;
    }
    
    // 2. 暂停设备
    suspend_console();
    error = dpm_suspend_noirq(PMSG_SUSPEND);
    if (error) {
        log_suspend_abort_reason("Device suspend noirq failed");
        goto Resume_console;
    }
    
    // 3. 禁用中断
    error = suspend_enter(state, &wakeup);
    
    // 4. 恢复设备
    dpm_resume_noirq(wakeup ? PMSG_RESUME : PMSG_RESTORE);
Resume_console:
    resume_console();
    dpm_resume_end(PMSG_RESUME);
Out:
    return error;
}

8.2 suspend_enter() 详细实现

suspend_enter() 函数

c 复制代码
// kernel/power/suspend.c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
    int error, last_dev;
    
    // 1. 禁用中断
    error = platform_suspend_begin(state);
    if (error)
        goto Close;
    
    // 2. 暂停系统核心服务
    suspend_console();
    suspend_test_start();
    error = dpm_suspend_late(PMSG_SUSPEND);
    if (error) {
        pr_err("late suspend of devices failed\n");
        goto Recover_platform;
    }
    
    // 3. 暂停设备(noirq 阶段)
    error = dpm_suspend_noirq(PMSG_SUSPEND);
    if (error) {
        pr_err("noirq suspend of devices failed\n");
        goto Recover_platform;
    }
    
    // 4. 禁用中断
    error = platform_suspend_prepare(state);
    if (error)
        goto Recover_platform;
    
    // 5. 同步所有 CPU
    error = suspend_disable_secondary_cpus();
    if (error || suspend_test(TEST_CPUS))
        goto Enable_cpus;
    
    // 6. 刷新缓存
    local_irq_disable();
    syscore_suspend();
    
    // 7. 进入休眠状态
    if (!suspend_test(TEST_CORE) && pm_wakeup_pending())
        goto Exit;
    
    error = suspend_ops->enter(state);
    
    // 8. 从休眠状态恢复
Exit:
    syscore_resume();
    local_irq_enable();
    
Enable_cpus:
    suspend_enable_secondary_cpus();
    
Recover_platform:
    platform_resume(state);
    dpm_resume_noirq(PMSG_RESUME);
    dpm_resume_early(PMSG_RESUME);
    suspend_test_finish("resume");
    resume_console();
    
Close:
    platform_resume_end(state);
    return error;
}

8.3 平台特定的 suspend 实现

ARM64 平台的 suspend 实现

c 复制代码
// arch/arm64/kernel/suspend.c
int cpu_suspend(unsigned long arg, int (*fn)(unsigned long))
{
    struct mm_struct *mm = current->mm;
    int ret;
    unsigned long flags;
    
    // 1. 保存当前进程的页表
    if (idmap_pgd) {
        cpu_set_reserved_ttbr0();
        local_flush_tlb_all();
    }
    
    // 2. 保存 CPU 状态
    ret = __cpu_suspend_enter(arg, fn);
    
    // 3. 恢复页表
    if (idmap_pgd) {
        cpu_unset_reserved_ttbr0();
        local_flush_tlb_all();
    }
    
    return ret;
}

__cpu_suspend_enter() 实现

c 复制代码
// arch/arm64/kernel/suspend.c
int __cpu_suspend_enter(unsigned long arg, int (*fn)(unsigned long))
{
    struct sleep_stack_data state;
    unsigned long flags;
    
    // 1. 保存 CPU 寄存器
    flags = local_daif_save();
    
    // 2. 保存栈指针
    state.stack = (unsigned long)__builtin_frame_address(0);
    
    // 3. 保存 CPU 上下文
    __cpu_suspend_enter_save(&state.cpu_context);
    
    // 4. 调用平台特定的 suspend 函数
    ret = fn(arg);
    
    // 5. 如果返回,恢复 CPU 上下文
    __cpu_suspend_exit(&state.cpu_context);
    
    local_daif_restore(flags);
    
    return ret;
}

8.4 唤醒流程的完整实现

唤醒中断处理

c 复制代码
// 示例:按键唤醒中断
static irqreturn_t power_button_irq(int irq, void *dev_id)
{
    struct device *dev = dev_id;
    
    // 1. 标记唤醒事件
    pm_wakeup_event(dev, 0);
    
    // 2. 如果系统正在休眠,唤醒系统
    if (pm_suspend_state())
        pm_system_wakeup();
    
    return IRQ_HANDLED;
}

pm_system_wakeup() 实现

c 复制代码
// kernel/power/main.c
void pm_system_wakeup(void)
{
    unsigned long flags;
    
    spin_lock_irqsave(&pm_wakeup_lock, flags);
    
    // 1. 设置唤醒标志
    pm_wakeup_irq = true;
    
    // 2. 如果系统正在休眠,中断休眠流程
    if (pm_suspend_state()) {
        pm_suspend_abort();
    }
    
    spin_unlock_irqrestore(&pm_wakeup_lock, flags);
}

平台特定的 resume 实现

c 复制代码
// arch/arm64/kernel/suspend.c
void cpu_resume(void)
{
    // 1. 恢复 CPU 上下文
    cpu_suspend_exit();
    
    // 2. 恢复 MMU
    cpu_do_resume(NULL);
    
    // 3. 恢复中断
    local_irq_enable();
}

8.5 设备驱动的完整 PM 实现示例

完整的设备驱动 PM 实现

c 复制代码
// drivers/example/example_driver.c
static int example_suspend(struct device *dev)
{
    struct example_device *edev = dev_get_drvdata(dev);
    
    // 1. 停止设备操作
    example_stop(edev);
    
    // 2. 保存寄存器状态
    edev->saved_regs.config = ioread32(edev->base + REG_CONFIG);
    edev->saved_regs.control = ioread32(edev->base + REG_CONTROL);
    
    // 3. 进入低功耗模式
    iowrite32(edev->saved_regs.config | POWER_DOWN, edev->base + REG_CONFIG);
    
    return 0;
}

static int example_suspend_late(struct device *dev)
{
    struct example_device *edev = dev_get_drvdata(dev);
    
    // 在中断禁用前执行的操作
    // 例如:关闭时钟、关闭电源等
    
    clk_disable(edev->clk);
    regulator_disable(edev->reg);
    
    return 0;
}

static int example_suspend_noirq(struct device *dev)
{
    struct example_device *edev = dev_get_drvdata(dev);
    
    // 中断已禁用,执行最后的操作
    // 例如:保存最后的寄存器状态
    
    edev->saved_regs.status = ioread32(edev->base + REG_STATUS);
    
    return 0;
}

static int example_resume_noirq(struct device *dev)
{
    struct example_device *edev = dev_get_drvdata(dev);
    
    // 中断仍禁用,恢复最基本的操作
    // 例如:恢复寄存器状态
    
    iowrite32(edev->saved_regs.status, edev->base + REG_STATUS);
    
    return 0;
}

static int example_resume_early(struct device *dev)
{
    struct example_device *edev = dev_get_drvdata(dev);
    
    // 在中断恢复前执行的操作
    // 例如:打开时钟、打开电源等
    
    regulator_enable(edev->reg);
    clk_enable(edev->clk);
    
    return 0;
}

static int example_resume(struct device *dev)
{
    struct example_device *edev = dev_get_drvdata(dev);
    
    // 1. 退出低功耗模式
    iowrite32(edev->saved_regs.config, edev->base + REG_CONFIG);
    
    // 2. 恢复寄存器状态
    iowrite32(edev->saved_regs.control, edev->base + REG_CONTROL);
    
    // 3. 重新初始化设备
    example_init(edev);
    
    // 4. 启动设备
    example_start(edev);
    
    return 0;
}

static const struct dev_pm_ops example_pm_ops = {
    .suspend = example_suspend,
    .suspend_late = example_suspend_late,
    .suspend_noirq = example_suspend_noirq,
    .resume_noirq = example_resume_noirq,
    .resume_early = example_resume_early,
    .resume = example_resume,
};

9. 唤醒源(Wakeup Source)详解

9.1 唤醒源的数据结构

struct wakeup_source

c 复制代码
// drivers/base/power/wakeup.c
struct wakeup_source {
    const char *name;
    struct list_head entry;
    spinlock_t lock;
    struct wake_irq *wakeirq;
    struct timer_list timer;
    unsigned long timer_expires;
    ktime_t total_time;
    ktime_t max_time;
    ktime_t last_time;
    ktime_t start_prevent_time;
    ktime_t prevent_sleep_time;
    unsigned long event_count;
    unsigned long active_count;
    unsigned long relax_count;
    unsigned long expire_count;
    unsigned long wakeup_count;
    bool active:1;
    bool autosleep_enabled:1;
};

唤醒源的注册

c 复制代码
// drivers/base/power/wakeup.c
struct wakeup_source *wakeup_source_register(struct device *dev,
                                              const char *name)
{
    struct wakeup_source *ws;
    int ret;
    
    // 1. 分配唤醒源结构
    ws = kzalloc(sizeof(*ws), GFP_KERNEL);
    if (!ws)
        return NULL;
    
    // 2. 初始化唤醒源
    wakeup_source_init(ws, name);
    
    // 3. 如果提供了设备,关联设备
    if (dev) {
        ret = device_wakeup_attach(dev, ws);
        if (ret) {
            wakeup_source_destroy(ws);
            return NULL;
        }
    }
    
    // 4. 添加到唤醒源列表
    list_add_rcu(&ws->entry, &wakeup_sources);
    
    return ws;
}

9.2 唤醒源的激活和停用

激活唤醒源

c 复制代码
// drivers/base/power/wakeup.c
void __pm_stay_awake(struct wakeup_source *ws)
{
    unsigned long flags;
    
    if (!ws)
        return;
    
    spin_lock_irqsave(&ws->lock, flags);
    
    // 1. 更新唤醒源时间
    wakeup_source_report_event(ws, false);
    
    // 2. 激活唤醒源
    if (!ws->active) {
        ws->active = true;
        ws->active_count++;
        ws->last_time = ktime_get();
    }
    
    del_timer(&ws->timer);
    ws->timer_expires = 0;
    
    spin_unlock_irqrestore(&ws->lock, flags);
}

停用唤醒源

c 复制代码
// drivers/base/power/wakeup.c
void __pm_relax(struct wakeup_source *ws)
{
    unsigned long flags;
    
    if (!ws)
        return;
    
    spin_lock_irqsave(&ws->lock, flags);
    
    // 1. 更新总时间
    if (ws->active) {
        ktime_t now = ktime_get();
        ktime_t delta = ktime_sub(now, ws->last_time);
        ws->total_time = ktime_add(ws->total_time, delta);
        if (ktime_to_ns(delta) > ktime_to_ns(ws->max_time))
            ws->max_time = delta;
        ws->active = false;
    }
    
    // 2. 停用唤醒源
    del_timer(&ws->timer);
    ws->timer_expires = 0;
    
    spin_unlock_irqrestore(&ws->lock, flags);
}

9.3 唤醒源阻止休眠检查

检查是否有活动的唤醒源

c 复制代码
// drivers/base/power/wakeup.c
bool pm_get_wakeup_count(unsigned int *count, bool block)
{
    unsigned int cnt, inpr;
    
    if (block) {
        DEFINE_WAIT(wait);
        
        for (;;) {
            prepare_to_wait(&wakeup_count_wait_queue, &wait,
                          TASK_INTERRUPTIBLE);
            cnt = atomic_read(&combined_event_count);
            inpr = get_nr_in_progress();
            if (inpr == 0 || signal_pending(current))
                break;
            pm_print_active_wakeup_sources();
            schedule();
        }
        finish_wait(&wakeup_count_wait_queue, &wait);
    }
    
    split_counters(&cnt, &inpr);
    *count = cnt;
    return !inpr;
}

10. Suspend to Disk (Hibernate) 详解

10.1 Hibernate 的流程

hibernate() 函数

c 复制代码
// kernel/power/hibernate.c
int hibernate(void)
{
    int error;
    
    // 1. 检查是否支持 hibernate
    if (!hibernation_available()) {
        pr_debug("PM: Hibernation not available.\n");
        return -EPERM;
    }
    
    // 2. 锁定系统
    lock_system_sleep();
    
    // 3. 同步文件系统
    pr_info("PM: Syncing filesystems ... ");
    sys_sync();
    pr_cont("done.\n");
    
    // 4. 冻结进程
    error = freeze_processes();
    if (error)
        goto Unlock;
    
    // 5. 冻结内核线程
    error = freeze_kernel_threads();
    if (error)
        goto Thaw_processes;
    
    // 6. 保存系统状态到磁盘
    error = hibernation_snapshot(hibernation_mode == HIBERNATION_PLATFORM);
    if (error || freezer_test_done)
        goto Thaw_kernel;
    
    // 7. 进入休眠状态
    if (hibernation_mode == HIBERNATION_TEST) {
        pr_info("PM: Hibernation test mode.\n");
        goto Thaw_kernel;
    }
    
    error = hibernation_platform_enter();
    
Thaw_kernel:
    thaw_kernel_threads();
Thaw_processes:
    thaw_processes();
Unlock:
    unlock_system_sleep();
    
    return error;
}

10.2 内存快照的保存

hibernation_snapshot() 实现

c 复制代码
// kernel/power/hibernate.c
int hibernation_snapshot(int platform_mode)
{
    int error;
    
    // 1. 分配交换空间
    error = swsusp_alloc();
    if (error)
        return error;
    
    // 2. 准备快照
    error = snapshot_read_next(snapshot);
    if (error < PAGE_SIZE)
        return error < 0 ? error : -EFAULT;
    
    // 3. 保存内存页面
    error = swsusp_write(flags);
    if (error)
        return error;
    
    // 4. 标记交换分区
    error = swsusp_check();
    if (error)
        return error;
    
    return 0;
}

swsusp_write() 实现

c 复制代码
// kernel/power/swap.c
int swsusp_write(unsigned int flags)
{
    struct snapshot_handle handle;
    struct swap_map_handle swap;
    struct swsusp_info *header;
    int error;
    
    // 1. 分配交换写入器
    error = get_swap_writer(&swap);
    if (error)
        return error;
    
    // 2. 分配头部页面
    header = (struct swsusp_info *)get_image_page(GFP_ATOMIC, PG_ANY);
    if (!header) {
        error = -ENOMEM;
        goto out_finish;
    }
    
    // 3. 初始化快照句柄
    memset(&handle, 0, sizeof(struct snapshot_handle));
    error = snapshot_read_next(&handle);
    if (error < PAGE_SIZE)
        goto out_finish;
    
    // 4. 写入头部信息
    prepare_header(&header->orig_info);
    error = swap_write_page(&swap, header, NULL);
    if (error)
        goto out_finish;
    
    // 5. 保存内存页面
    error = save_image(&swap, &handle, nr_pages);
    if (error)
        goto out_finish;
    
    // 6. 写入结束标记
    error = flush_swap_writer(&swap);
    
out_finish:
    put_swap_writer(&swap);
    return error;
}

10.3 从磁盘恢复

hibernation_restore() 实现

c 复制代码
// kernel/power/hibernate.c
int hibernation_restore(int platform_mode)
{
    int error;
    
    // 1. 准备恢复
    pm_prepare_console();
    suspend_console();
    pm_restrict_gfp_mask();
    error = pm_suspend_freeze(PM_SUSPEND_FREEZE);
    if (error)
        goto Restore;
    
    // 2. 从磁盘恢复内存状态
    error = load_image_and_restore();
    if (error)
        goto Restore;
    
Restore:
    pm_restore_gfp_mask();
    thaw_console();
    pm_restore_console();
    
    return error;
}
相关推荐
Shawn_CH2 小时前
Linux 休眠唤醒机制详解(中)
嵌入式
charlie1145141917 小时前
在上位机上熟悉FreeRTOS API
笔记·学习·嵌入式·c·freertos·工程
Shawn_CH8 小时前
Linux 零拷贝技术详解
嵌入式
Shawn_CH8 小时前
Linux ROS与进程间通信详解
嵌入式
华清远见成都中心20 小时前
成都理工大学&华清远见成都中心实训,助力电商人才培养
大数据·人工智能·嵌入式
切糕师学AI20 小时前
ARM 架构中的 CONTROL 寄存器
arm开发·硬件架构·嵌入式·芯片·寄存器
fzm52981 天前
C语言单元测试在嵌入式软件开发中的作用及专业工具的应用
自动化测试·单元测试·汽车·嵌入式·白盒测试
大聪明-PLUS1 天前
Linux 系统中的电池衰减
linux·嵌入式·arm·smarc
Shawn_CH1 天前
Linux 共享内存详解
嵌入式