【Android】 android suspend/resume总结(3)

一、摘要

本文深入分析了Linux内核中设备电源管理的核心实现文件drivers/base/power/main.c,详细阐述了系统级挂起(suspend)和恢复(resume)的完整流程。通过对源码的深入剖析,揭示了Linux内核如何通过分阶段处理、设备依赖管理、异步优化等机制来实现高效可靠的设备电源管理。

二、概述

这个文件是 Linux 设备模型中电源管理的核心实现,负责管理系统中所有设备的挂起(suspend)和恢复(resume)操作。

1、核心数据结构

c 复制代码
LIST_HEAD(dpm_list);                    // 主设备列表
static LIST_HEAD(dpm_prepared_list);    // 已准备的设备列表
static LIST_HEAD(dpm_suspended_list);   // 已挂起的设备列表
static LIST_HEAD(dpm_late_early_list);  // 晚期/早期处理列表
static LIST_HEAD(dpm_noirq_list);       // 无中断处理列表

2、Suspend 流程详细分析

c 复制代码
graph TD
    A[dpm_suspend_start] --> B[dpm_prepare]
    B --> C[dpm_suspend]
    A --> D[dpm_suspend_end]
    D --> E[dpm_suspend_late]
    E --> F[dpm_suspend_noirq]
    
    B --> B1[device_prepare]
    C --> C1[device_suspend]
    E --> E1[device_suspend_late]
    F --> F1[device_suspend_noirq]
    
    B1 --> B2[移动到dpm_prepared_list]
    C1 --> C2[移动到dpm_suspended_list]
    E1 --> E2[移动到dpm_late_early_list]
    F1 --> F2[移动到dpm_noirq_list]
c 复制代码
正常运行 → 准备阶段 → 挂起阶段 → 晚期挂起 → 无中断挂起
    ↓         ↑         ↑         ↑         ↑
完成阶段 ← 恢复阶段 ← 早期恢复 ← 无中断恢复 ← [最深睡眠]

1、详细函数分析

c 复制代码
int dpm_suspend_start(pm_message_t state)
{
    ktime_t starttime = ktime_get();
    int error;

    error = dpm_prepare(state);
    if (error)
        dpm_save_failed_step(SUSPEND_PREPARE);
    else
        error = dpm_suspend(state);

    dpm_show_time(starttime, state, error, "start");
    return error;
}
bash 复制代码
功能: 启动设备挂起流程的入口函数
	调用 dpm_prepare() 准备所有设备
	调用 dpm_suspend() 挂起所有设备
	记录执行时间和错误信息
c 复制代码
int dpm_prepare(pm_message_t state)
{
    int error = 0;
    
    wait_for_device_probe();        // 等待设备探测完成
    device_block_probing();         // 阻止新设备探测
    
    mutex_lock(&dpm_list_mtx);
    while (!list_empty(&dpm_list) && !error) {
        struct device *dev = to_device(dpm_list.next);
        
        get_device(dev);
        mutex_unlock(&dpm_list_mtx);
        
        error = device_prepare(dev, state);
        
        mutex_lock(&dpm_list_mtx);
        if (!error) {
            dev->power.is_prepared = true;
            list_move_tail(&dev->power.entry, &dpm_prepared_list);
        }
        put_device(dev);
    }
    mutex_unlock(&dpm_list_mtx);
    return error;
}
bash 复制代码
功能: 准备阶段
	阻止新设备的探测和注册
	遍历 dpm_list 中的所有设备
	调用每个设备的 prepare 回调
	将准备好的设备移动到 dpm_prepared_list
c 复制代码
static int device_prepare(struct device *dev, pm_message_t state)
{
    int (*callback)(struct device *) = NULL;
    int ret = 0;

    pm_runtime_get_noresume(dev);   // 阻止运行时挂起
    
    if (dev->power.syscore)
        return 0;

    device_lock(dev);
    
    // 按优先级选择回调函数
    if (dev->pm_domain)
        callback = dev->pm_domain->ops.prepare;
    else if (dev->type && dev->type->pm)
        callback = dev->type->pm->prepare;
    else if (dev->class && dev->class->pm)
        callback = dev->class->pm->prepare;
    else if (dev->bus && dev->bus->pm)
        callback = dev->bus->pm->prepare;
    
    if (!callback && dev->driver && dev->driver->pm)
        callback = dev->driver->pm->prepare;

    if (callback)
        ret = callback(dev);

    device_unlock(dev);
    
    // 设置直接完成标志
    spin_lock_irq(&dev->power.lock);
    dev->power.direct_complete = state.event == PM_EVENT_SUSPEND &&
        (ret > 0 || dev->power.no_pm_callbacks) &&
        !dev_pm_test_driver_flags(dev, DPM_FLAG_NO_DIRECT_COMPLETE);
    spin_unlock_irq(&dev->power.lock);
    
    return 0;
}
bash 复制代码
功能: 单个设备的准备
	选择合适的 prepare 回调函数(优先级:pm_domain > type(设备模型中的一个抽象层,主要用于将设备归类) > class(用于表示设备所属的"设备类") > bus > driver)
	执行回调函数
	设置 direct_complete 标志用于优化
c 复制代码
int dpm_suspend(pm_message_t state)
{
    ktime_t starttime = ktime_get();
    int error = 0;

    devfreq_suspend();      // 挂起设备频率调节
    cpufreq_suspend();      // 挂起CPU频率调节

    pm_transition = state;
    async_error = 0;

    mutex_lock(&dpm_list_mtx);
    while (!list_empty(&dpm_prepared_list)) {
        struct device *dev = to_device(dpm_prepared_list.prev);
        
        list_move(&dev->power.entry, &dpm_suspended_list);
        
        if (dpm_async_fn(dev, async_suspend))
            continue;
            
        get_device(dev);
        mutex_unlock(&dpm_list_mtx);
        
        error = device_suspend(dev, state, false);
        
        put_device(dev);
        mutex_lock(&dpm_list_mtx);
        
        if (error || async_error)
            break;
    }
    mutex_unlock(&dpm_list_mtx);
    
    async_synchronize_full();
    return error;
}
bash 复制代码
功能: 主要挂起阶段
	挂起 CPU 和设备频率调节
	支持异步挂起以提高性能
	将设备从 dpm_prepared_list 移动到 dpm_suspended_list
c 复制代码
static int dpm_noirq_suspend_devices(pm_message_t state)
{
    /* 记录函数开始执行的时间,用于性能统计 */
    ktime_t starttime = ktime_get();
    int error = 0;

    /* 开始追踪无中断挂起阶段,用于系统调试和性能分析 */
    trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, true);

    /* 设置全局电源管理转换状态,供其他函数使用 */
    pm_transition = state;
    
    /* 重置异步错误标志,为新的挂起流程做准备 */
    async_error = 0;

    /* 
     * 获取设备电源管理列表的互斥锁
     * 这是关键的同步机制,确保多线程环境下列表操作的原子性
     */
    mutex_lock(&dpm_list_mtx);

    /*
     * 主要处理循环:处理所有在晚期挂起列表中的设备
     * 
     * 处理顺序说明:
     * - 从dpm_late_early_list的尾部开始处理(LIFO - 后进先出)
     * - 这确保了子设备在父设备之前被挂起
     * - 维护了正确的设备依赖关系顺序
     */
    while (!list_empty(&dpm_late_early_list)) {
        /* 
         * 获取列表中的最后一个设备(最晚进入晚期挂起的设备)
         * 使用LIFO顺序确保依赖关系的正确性:
         * 子设备先挂起,父设备后挂起
         */
        struct device *dev = to_device(dpm_late_early_list.prev);

        /*
         * 关键的列表状态转换:
         * 将设备从 dpm_late_early_list 移动到 dpm_noirq_list
         * 
         * 状态转换意义:
         * dpm_late_early_list: 已完成晚期挂起,准备无中断挂起
         * dpm_noirq_list: 正在进行或已完成无中断挂起
         */
        list_move(&dev->power.entry, &dpm_noirq_list);

        /*
         * 尝试异步处理设备挂起
         * 
         * 异步处理的优势:
         * 1. 提高系统挂起性能,减少总体挂起时间
         * 2. 允许独立设备并行处理
         * 3. 不影响有依赖关系的设备的正确顺序
         * 
         * 如果设备支持异步处理且异步调度成功,则continue跳过同步处理
         */
        if (dpm_async_fn(dev, async_suspend_noirq))
            continue;

        /*
         * 同步处理路径:
         * 对于不支持异步处理或异步调度失败的设备
         */
        
        /* 增加设备引用计数,防止设备在处理过程中被意外释放 */
        get_device(dev);

        /*
         * 临时释放互斥锁的重要原因:
         * 1. device_suspend_noirq可能是耗时操作
         * 2. 允许异步处理的设备继续执行
         * 3. 避免长时间持有锁导致的性能问题
         * 4. 防止死锁情况的发生
         */
        mutex_unlock(&dpm_list_mtx);

        /*
         * 执行设备的无中断挂起操作
         * 
         * 这是核心的设备挂起逻辑:
         * - 调用设备特定的suspend_noirq回调函数
         * - 在中断被禁用的环境中执行
         * - 处理设备的最终挂起状态设置
         * - async参数为false表示这是同步处理
         */
        error = device_suspend_noirq(dev, state, false);

        /* 减少设备引用计数,与前面的get_device配对 */
        put_device(dev);

        /* 重新获取互斥锁,准备处理下一个设备或检查错误 */
        mutex_lock(&dpm_list_mtx);

        /*
         * 错误检查和处理:
         * 
         * 检查两种类型的错误:
         * 1. error: 当前设备同步处理的错误
         * 2. async_error: 异步处理设备产生的错误
         * 
         * 任何错误都会导致整个挂起流程中断,这是为了:
         * - 保证系统状态的一致性
         * - 避免部分设备挂起导致的不稳定状态
         * - 及时发现和处理硬件或驱动问题
         */
        if (error || async_error)
            break;
    }

    /* 释放设备电源管理列表互斥锁 */
    mutex_unlock(&dpm_list_mtx);

    /*
     * 等待所有异步操作完成
     * 
     * 这个调用的重要性:
     * 1. 确保所有异步启动的设备挂起操作都已完成
     * 2. 收集异步操作中可能产生的错误
     * 3. 保证函数返回时所有设备都已处理完毕
     * 4. 维护系统状态的一致性
     */
    async_synchronize_full();

    /*
     * 最终错误状态确定:
     * 如果同步处理没有错误,但异步处理有错误,
     * 则将异步错误作为最终错误返回
     */
    if (!error)
        error = async_error;

    /*
     * 错误记录和调试支持:
     * 如果挂起过程中发生错误,记录失败的步骤
     * 这对于系统调试和问题诊断非常重要
     */
    if (error)
        dpm_save_failed_step(SUSPEND_SUSPEND_NOIRQ);

    /*
     * 性能统计和监控:
     * 显示无中断挂起阶段的执行时间
     * 包括成功/失败状态,用于性能分析和优化
     */
    dpm_show_time(starttime, state, error, "noirq");

    /* 结束追踪无中断挂起阶段 */
    trace_suspend_resume(TPS("dpm_suspend_noirq"), state.event, false);

    /* 返回最终的错误状态 */
    return error;
}
c 复制代码
static int device_suspend(struct device *dev, pm_message_t state, bool async)
{
    pm_callback_t callback = NULL;
    const char *info = NULL;
    int error = 0;
    DECLARE_DPM_WATCHDOG_ON_STACK(wd);

    dpm_wait_for_subordinate(dev, async);   // 等待子设备完成
    
    pm_runtime_barrier(dev);                // 等待运行时PM完成
    
    if (pm_wakeup_pending()) {
        async_error = -EBUSY;
        goto Complete;
    }

    // 处理 direct_complete 优化
    if (dev->power.direct_complete) {
        if (pm_runtime_status_suspended(dev)) {
            pm_runtime_disable(dev);
            if (pm_runtime_status_suspended(dev)) {
                dev->power.is_suspended = true;
                goto Complete;
            }
            pm_runtime_enable(dev);
        }
        dev->power.direct_complete = false;
    }

    dpm_watchdog_set(&wd, dev);
    device_lock(dev);

    // 选择回调函数(优先级同prepare)
    if (dev->pm_domain) {
        info = "power domain ";
        callback = pm_op(&dev->pm_domain->ops, state);
    } else if (dev->type && dev->type->pm) {
        info = "type ";
        callback = pm_op(dev->type->pm, state);
    }
    // ... 其他优先级选择

    error = dpm_run_callback(callback, dev, state, info);

    if (!error) {
        dev->power.is_suspended = true;
        if (device_may_wakeup(dev))
            dev->power.wakeup_path = true;
        dmp_propagate_wakeup_to_parent(dev);
    }

    device_unlock(dev);
    dpm_watchdog_clear(&wd);
    
Complete:
    complete_all(&dev->power.completion);
    return error;
}
bash 复制代码
功能: 单个设备挂起
	等待子设备和依赖设备完成
	处理 direct_complete 优化
	设置看门狗防止挂起超时
	执行设备特定的挂起回调
	处理唤醒路径传播

3、Resume 流程详细分析

bash 复制代码
graph TD
    A[dpm_resume_start] --> B[dpm_resume_noirq]
    B --> C[dpm_resume_early]
    A --> D[dpm_resume_end]
    D --> E[dpm_resume]
    E --> F[dpm_complete]
    
    B --> B1[device_resume_noirq]
    C --> C1[device_resume_early]
    E --> E1[device_resume]
    F --> F1[device_complete]
    
    B1 --> B2[从dpm_noirq_list移动到dmp_late_early_list]
    C1 --> C2[从dpm_late_early_list移动到dpm_suspended_list]
    E1 --> E2[从dpm_suspended_list移动到dpm_prepared_list]
    F1 --> F2[从dpm_prepared_list移动到dpm_list]

1、详细函数分析

c 复制代码
void dpm_resume_start(pm_message_t state)
{
    dpm_resume_noirq(state);
    dpm_resume_early(state);
}
c 复制代码
void dpm_resume_noirq(pm_message_t state)
{
    dpm_noirq_resume_devices(state);
    
    resume_device_irqs();           // 恢复设备中断
    device_wakeup_disarm_wake_irqs(); // 解除唤醒中断
}
c 复制代码
static void dpm_noirq_resume_devices(pm_message_t state)
{
    struct device *dev;
    ktime_t starttime = ktime_get();

    /* 开始追踪无中断恢复阶段 */
    trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, true);

    /* 重置异步错误标志 */
    async_error = 0;
    /* 设置当前电源管理转换状态 */
    pm_transition = state;

    /* 获取设备电源管理列表互斥锁,确保线程安全 */
    mutex_lock(&dpm_list_mtx);

    /*
     * 第一阶段:启动异步设备的恢复
     * 优先触发"异步"设备的恢复,这样它们就不必等待
     * 那些它们不依赖的"非异步"设备完成
     * 这是一个重要的性能优化策略
     */
    list_for_each_entry(dev, &dpm_noirq_list, power.entry)
        dpm_async_fn(dev, async_resume_noirq);

    /*
     * 第二阶段:处理同步设备和等待异步设备完成
     * 从dpm_noirq_list的头部开始处理设备(FIFO顺序)
     * 这确保了设备按照正确的依赖顺序进行恢复
     */
    while (!list_empty(&dpm_noirq_list)) {
        /* 获取列表中的第一个设备(最早进入的设备) */
        dev = to_device(dpm_noirq_list.next);
        
        /* 
         * 将设备从dpm_noirq_list移动到dpm_late_early_list
         * 这标志着设备已经完成了无中断恢复阶段,
         * 准备进入下一个恢复阶段(早期恢复阶段)
         */
        list_move_tail(&dev->power.entry, &dpm_late_early_list);

        /*
         * 检查设备是否正在进行异步处理
         * 如果设备没有在异步处理中,则进行同步处理
         */
        if (!dev->power.async_in_progress) {
            /* 增加设备引用计数,防止设备在处理过程中被释放 */
            get_device(dev);

            /* 
             * 临时释放互斥锁,允许其他线程访问设备列表
             * 这在执行可能耗时的设备操作时很重要
             */
            mutex_unlock(&dpm_list_mtx);

            /* 
             * 执行设备的无中断恢复操作
             * 这是核心的设备恢复逻辑,会调用设备特定的resume_noirq回调
             * async参数为false表示这是同步处理
             */
            device_resume_noirq(dev, state, false);

            /* 减少设备引用计数 */
            put_device(dev);

            /* 重新获取互斥锁,继续处理下一个设备 */
            mutex_lock(&dpm_list_mtx);
        }
        /*
         * 如果设备正在异步处理中,则跳过同步处理
         * 异步处理会在后面的async_synchronize_full()中等待完成
         */
    }

    /* 释放设备电源管理列表互斥锁 */
    mutex_unlock(&dpm_list_mtx);

    /*
     * 等待所有异步操作完成
     * 这确保了所有设备(无论是同步还是异步处理的)
     * 都已经完成了无中断恢复阶段
     */
    async_synchronize_full();

    /* 
     * 显示无中断恢复阶段的执行时间统计
     * 这对于性能分析和调试很有用
     */
    dpm_show_time(starttime, state, 0, "noirq");

    /*
     * 检查是否有异步错误发生
     * 如果有错误,保存失败步骤信息用于调试
     */
    if (async_error)
        dpm_save_failed_step(SUSPEND_RESUME_NOIRQ);

    /* 结束追踪无中断恢复阶段 */
    trace_suspend_resume(TPS("dpm_resume_noirq"), state.event, false);
}
c 复制代码
static void device_resume_noirq(struct device *dev, pm_message_t state, bool async)
{
    pm_callback_t callback = NULL;
    const char *info = NULL;
    bool skip_resume;
    int error = 0;

    if (!dev->power.is_noirq_suspended)
        goto Out;

    if (!dpm_wait_for_superior(dev, async))
        goto Out;

    skip_resume = dev_pm_skip_resume(dev);
    
    // 根据skip_resume设置运行时PM状态
    if (skip_resume)
        pm_runtime_set_suspended(dev);
    else if (dev_pm_skip_suspend(dev))
        pm_runtime_set_active(dev);

    // 选择回调函数
    if (dev->pm_domain) {
        info = "noirq power domain ";
        callback = pm_noirq_op(&dev->pm_domain->ops, state);
    }
    // ... 其他选择逻辑

    if (callback)
        goto Run;

    if (skip_resume)
        goto Skip;

    if (dev->driver && dev->driver->pm) {
        info = "noirq driver ";
        callback = pm_noirq_op(dev->driver->pm, state);
    }

Run:
    error = dpm_run_callback(callback, dev, state, info);

Skip:
    dev->power.is_noirq_suspended = false;

Out:
    complete_all(&dev->power.completion);
}
c 复制代码
static bool dpm_wait_for_superior(struct device *dev, bool async)
{
    struct device *parent;
    
    mutex_lock(&dpm_list_mtx);
    if (!device_pm_initialized(dev)) {
        mutex_unlock(&dpm_list_mtx);
        return false;
    }
    parent = get_device(dev->parent);
    mutex_unlock(&dpm_list_mtx);
    
    dpm_wait(parent, async);            // 等待父设备
    put_device(parent);
    
    dpm_wait_for_suppliers(dev, async); // 等待供应商设备
    
    return device_pm_initialized(dev);
}
c 复制代码
    A[设备回调选择] --> B{pm_domain存在?}
    B -->|是| C[使用pm_domain回调]
    B -->|否| D{type存在?}
    D -->|是| E[使用type回调]
    D -->|否| F{class存在?}
    F -->|是| G[使用class回调]
    F -->|否| H{bus存在?}
    H -->|是| I[使用bus回调]
    H -->|否| J[使用driver回调]

总结

bash 复制代码
/* 列表状态转换 */
dpm_list → dpm_prepared_list     (prepare完成)
dpm_prepared_list → dpm_suspended_list   (suspend完成)
dmp_suspended_list → dpm_late_early_list (suspend_late完成)
dpm_late_early_list → dpm_noirq_list     (suspend_noirq完成)

dpm_noirq_list → dpm_late_early_list     (resume_noirq完成)
dpm_late_early_list → dpm_suspended_list (resume_early完成)  
dpm_suspended_list → dpm_prepared_list   (resume完成)
dpm_prepared_list → dpm_list             (complete完成)
bash 复制代码
drivers/base/power/main.c 是 Linux 内核电源管理的核心,它实现了:

	分阶段的设备电源管理: prepare → suspend → suspend_late → suspend_noirq
	设备依赖关系处理: 确保父设备和供应商设备的正确顺序
	异步处理支持: 提高系统挂起/恢复性能
	多种优化机制: direct_complete、smart_suspend 等
	完善的错误处理: 看门狗、错误记录、回滚机制
	这个文件确保了系统中所有设备能够安全、有序地进行电源状态转换,是 Linux 电源管理子系统的基石。
相关推荐
00后程序员张2 小时前
iOS 26 兼容测试实战,机型兼容、SwiftUI 兼容性改动
android·ios·小程序·uni-app·swiftui·cocoa·iphone
老黄编程2 小时前
ubuntu如何查看一个内核模块被什么模块依赖(内核模块信息常用命令)?
linux·运维·ubuntu
知北游天3 小时前
Linux网络:使用UDP实现网络通信(服务端&&客户端)
linux·网络·udp
半桔3 小时前
【网络编程】TCP 粘包处理:手动序列化反序列化与报头封装的完整方案
linux·网络·c++·网络协议·tcp/ip
molong9313 小时前
Android 应用配置跳转微信小程序
android·微信小程序·小程序
2501_915106323 小时前
iOS 可分发是已经上架了吗?深入解析应用分发状态、ipa 文件上传、TestFlight 测试与 App Store 审核流程
android·ios·小程序·https·uni-app·iphone·webview
<但凡.3 小时前
Linux 修炼:进程控制(一)
linux·运维·服务器·bash
✎﹏赤子·墨筱晗♪4 小时前
Ansible Playbook 入门指南:从基础到实战
linux·服务器·ansible
乌萨奇也要立志学C++5 小时前
【Linux】进程概念(六):进程地址空间深度解析:虚拟地址与内存管理的奥秘
linux·运维