当前位置: 首页 > web >正文

Linux电源管理(6)_Generic PM之挂起功能

原文链接:Linux电源管理(6)_Generic PM之挂起功能

1.前言

Linux内核提供了三种暂停方式:Freeze,Standby和STR(暂停到RAM),在用户空间向” / sys / power / state”文件分别写入“ freeze”,“ standby”和“ mem”,可以触发。

另外内核中,挂起和恢复过程涉及到PM核心,设备PM,各个设备的驱动,平台相关的PM,CPU控制等多个模块,涉及控制台切换,进程冻结,CPU热插拔,唤醒处理等过个知识点。就让我们跟着内核代码,一一见识它们吧。

0

2.暂停功能有关的代码分布

内核中止功能相关的代码包括PM内核,设备PM,平台PM等几大块,具体如下:

1)PM核心

内核/电源/main.c----提供用户空间接口(/ sys /电源/状态)

kernel / power / suspend.c ----挂起功能的主逻辑

内核/电源/suspend_test.c----挂起功能的测试逻辑

内核/电源/控制台.c ----挂起过程中对控制台的处理逻辑

内核/电源/进程.c ----挂起过程中对进程的处理逻辑

2)平台相关的PM

include / linux / suspend.h ----定义平台相关的PM有关的操作函数集

arch / xxx / mach-xxx / xxx.c或者

arch / xxx / plat-xxx / xxx.c ----平台相关的电源管理操作

3.暂停和恢复过程概述

下面的图片对Linuxsuspend&resume过程由一个概述,读者可以顺着这个流程阅读内核源代码。具体的说明,可以参考后面的代码分析。

0

4.代码分析

4.1暂停入口

在用户空间执行如下操作:

回声“冻结”> / sys / power / state

回声“待机”> / sys / power / state

回声“内存”> / sys / power / state

会通过sysfs触发suspend的执行,相应的处理代码如下:

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;    // 2. 检查当前是否已处于非ON状态(禁止重复操作)    if (pm_autosleep_state() > PM_SUSPEND_ON) {        error = -EBUSY;        goto out;    }    // 3. 解析输入状态并执行对应操作    state = decode_state(buf, n);    if (state < PM_SUSPEND_MAX) {        error = pm_suspend(state);      // 常规挂起    } else if (state == PM_SUSPEND_MAX) {        error = hibernate();            // 休眠到磁盘    } else {        error = -EINVAL;                // 无效状态    }out:    // 4. 释放锁并返回结果    pm_autosleep_unlock();    return error ? error : n;}power_attr (状态);

power_attr定义了一个名称为状态的属性文件,该文件的存储接口为state_store,该接口在锁定住自动休眠功能后,解析用户传入的缓冲器(冷冻,备用或MEM),转换成状态参数。

状态参数的类型为suspend_state_t,在包括\ linux \ suspend.h中定义,为电源管理状态在内核中的表示。具体如下:​​​​​​​

typedef int __suspend_state_t;#define PM_SUSPEND_ON         ((__force suspend_state_t)0)#define PM_SUSPEND_FREEZE     ((__force suspend_state_t)1)#define PM_SUSPEND_STANDBY    ((__force suspend_state_t)2)#define PM_SUSPEND_MEM        ((__force suspend_state_t)3)#define PM_SUSPEND_MIN        PM_SUSPEND_FREEZE#define PM_SUSPEND_MAX        ((__force suspend_state_t)4)

根据状态的值,如果不是(PM_SUSPEND_MAX,对应于hibernate功能),则调用pm_suspend接口,进行后续的处理。 

pm_suspend在内核/power/suspend.c定义,处理所有的suspend过程。  

4.2 pm_suspend&enter_state

pm_suspend的实现非常简单,简单的做一下参数合法性判断,直接调用enter_state接口

int pm_suspend(suspend_state_t state){    int error;    // 1. 检查状态有效性    if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)        return -EINVAL;    // 2. 进入挂起状态    error = enter_state(state);    // 3. 更新统计信息    if (error) {        suspend_stats.fail++;        dpm_save_failed_errno(error);    } else {        suspend_stats.success++;    }    return error;}

enter_state代码为:​​​​

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. 特殊处理FREEZE状态    if (state == PM_SUSPEND_FREEZE)        freeze_begin();    // 4. 同步文件系统    printk(KERN_INFO "PM: Syncing filesystems... ");    sys_sync();    printk("done.\n");    // 5. 系统挂起准备    pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);    error = suspend_prepare(state);    if (error)        goto Unlock;    // 6. 测试冻结器(可选调试)    if (suspend_test(TEST_FREEZER))        goto Finish;    // 7. 核心挂起流程    pr_debug("PM: Entering %s sleep\n", pm_states[state]);    pm_restrict_gfp_mask();    error = suspend_devices_and_enter(state);    pm_restore_gfp_mask();Finish:    // 8. 唤醒后处理    pr_debug("PM: Finishing wakeup.\n");    suspend_finish();Unlock:    // 9. 释放锁并返回    mutex_unlock(&pm_mutex);    return error;}

主要工作包括:

a)调用valid_state,判断该平台是否支持该电源状态。

暂停的最终目的,是让系统进入可恢复的挂起状态,而该功能必须有平台相关代码的参与才能完成,因此内核PM Core就提供了一系列的替代函数(封装在platform_suspend_ops中),让平台代码(如arch / arm / mach-xxx / pm.c)实现,然后由PM Core在适当的时机调用。这些变量函数包含一个有效函数,就是可以识别PM Core,支持某种状态。

最后看一下valid_state的实现(删除了无关代码):​​​​​​​

bool valid_state(suspend_state_t state){    // 1. FREEZE 状态始终有效    if (state == PM_SUSPEND_FREEZE) {        return true;    }    // 2. 其他状态需要底层支持    /*     * PM_SUSPEND_STANDBY 和 PM_SUSPEND_MEM 需要:     * - suspend_ops 存在     * - 且实现了 valid 回调     * - 且回调返回 true     */    return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);}

如果是冻结,无需平台代码参与即可支持,直接返回true。对于standby和mem,则需要调用suspend_ops的有效回送,由替代平台代码判断是否支持。 

b)加互斥锁,只允许一个实例处理暂停。

c)如果状态为冻结,则调用freeze_begin,进行暂停以冻结相关的特殊动作。我会在后面统一分析freeze的特殊动作,此处暂不描述。

d)打印提示信息,同步文件系统。

e)调用suspend_prepare,进行suspend前的准备,主要包括切换控制台和进程与线程冻结。如果失败,则终止suspend过程。

f)然后,调用suspend_devices_and_enter接口,该接口负责suspend和resume的所有实际动作。前半部分,suspend console,suspend device,关中断,调用平台相关的suspend_ops使系统进入低流量状态。后半部分,在系统被事件唤醒后,处理相关动作,调用平台相关的suspend_ops恢复系统,开中断,恢复设备,恢复控制台。

g)最后,调用suspend_finish,恢复(或等待恢复)process&thread,还原控制台。  

4.3 suspend_prepare

suspend_prepare的代码如下:​​​​​​​

static int suspend_prepare(suspend_state_t state){    int error;    // 1. 检查挂起操作是否可用    if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))        return -EPERM;    // 2. 准备控制台    pm_prepare_console();    // 3. 调用挂准备通知链    error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);    if (error)        goto Finish;    // 4. 冻结用户进程    error = suspend_freeze_processes();    if (!error)        return 0;    // 5. 冻结失败处理    suspend_stats.failed_freeze++;    dpm_save_failed_step(SUSPEND_FREEZE);Finish:    // 6. 清理工作    pm_notifier_call_chain(PM_POST_SUSPEND);    pm_restore_console();    return error;}

主要工作为:

a)检查suspend_ops是否提供了。enter替代,没有的话,返回错误。

b)调用pm_prepare_console,将当前console切换到一个虚拟控制台并重新连接内核的kmsg(需要的话)。该功能切换VT开关,后面我会在稍微详细的介绍一下,但Linux控制台是相当复杂的,更具体的分析,要在控制台上方的分析文章中说明。

c)调用pm_notifier_call_chain,发送暂停开始的消息(PM_SUSPEND_PREPARE),后面会详细描述。

d)调用suspend_freeze_processes,冻结用户空间进程和一些内核线程。该功能变量冻结任务,我会专门用一篇文章去分析它。此处不再详细说明了。

e)如果任务冻结失败,调用pm_restore_console,将console切换回原来的console,并返回错误,刹车能终止挂起。  

4.4suspend_devices_and_enter

suspend_devices_and_enter的过程较多复杂,代码实现如下:​​​​​​​

int suspend_devices_and_enter(suspend_state_t state){    int error;    bool wakeup = false;    // 1. 检查挂起操作是否可用    if (need_suspend_ops(state) && !suspend_ops)        return -ENOSYS;    trace_machine_suspend(state);    // 2. 调用平台特定的挂起开始函数    if (need_suspend_ops(state) && suspend_ops->begin) {        error = suspend_ops->begin(state);        if (error)            goto Close;    }    // 3. 准备系统环境    suspend_console();    ftrace_stop();    suspend_test_start();    // 4. 挂起设备    error = dpm_suspend_start(PMSG_SUSPEND);    if (error) {        printk(KERN_ERR "PM: Some devices failed to suspend\n");        goto Recover_platform;    }    suspend_test_finish("suspend devices");    // 5. 设备挂起测试    if (suspend_test(TEST_DEVICES))        goto Recover_platform;    // 6. 进入挂起状态(可能循环)    do {        error = suspend_enter(state, &wakeup);    } while (!error && !wakeup && need_suspend_ops(state) &&             suspend_ops->suspend_again && suspend_ops->suspend_again());Resume_devices:    // 7. 恢复设备    suspend_test_start();    dpm_resume_end(PMSG_RESUME);    suspend_test_finish("resume devices");    ftrace_start();    resume_console();Close:    // 8. 调用平台特定的挂起结束函数    if (need_suspend_ops(state) && suspend_ops->end)        suspend_ops->end();    trace_machine_suspend(PWR_EVENT_EXIT);    return error;Recover_platform:    // 9. 挂起失败时的平台恢复    if (need_suspend_ops(state) && suspend_ops->recover)        suspend_ops->recover();    goto Resume_devices;}

0

a)再次检查平台代码是否需要提供以及是否提供了suspend_ops。

b)调用suspend_ops的开始(有的话),通知平台代码,刹车让其作相应的处理(需要的话)。可能失败,需要跳至Close处执行恢复操作(suspend_ops-> end)。

该接口由“ kernel \ printk.c”实现,主要是保持住一个锁,该锁会阻止其他代码访问控制台。

d)调用ftrace_stop,停止ftrace功能。ftrace是一个很有意思的功能,后面再介绍。

e)调用dpm_suspend_start,调用所有设备的-> prepare和-> suspend变量函数(具体可参考“ Linux电源管理(4)_Power Management Interface ”的描述),suspend需要正常suspend的设备。suspend设备可能失败,需要跳至Recover_platform,执行recover操作(suspend_ops-> recover)。

f)以上都是suspend前的准备工作,此时,调用suspend_enter接口,使系统进入指定的电源状态。该接口的内容如下:​​​​​​

static int suspend_enter(suspend_state_t state, bool *wakeup){    int error;    // 1. 平台早期准备    if (need_suspend_ops(state) && suspend_ops->prepare) {        error = suspend_ops->prepare();        if (error)            goto Platform_finish;    }    // 2. 设备挂起最终阶段    error = dpm_suspend_end(PMSG_SUSPEND);    if (error) {        printk(KERN_ERR "PM: Some devices failed to power down\n");        goto Platform_finish;    }    // 3. 平台晚期准备    if (need_suspend_ops(state) && suspend_ops->prepare_late) {        error = suspend_ops->prepare_late();        if (error)            goto Platform_wake;    }    // 4. 平台测试(调试用)    if (suspend_test(TEST_PLATFORM))        goto Platform_wake;    // 5. 处理FREEZE状态    /*     * PM_SUSPEND_FREEZE 等同于:     * 冻结进程 + 挂起设备 + 空闲处理器     * 所以应在所有设备挂起后立即调用     */    if (state == PM_SUSPEND_FREEZE) {        freeze_enter();        goto Platform_wake;    }    // 6. 禁用非启动CPU    error = disable_nonboot_cpus();    if (error || suspend_test(TEST_CPUS))        goto Enable_cpus;    // 7. 核心挂起流程    arch_suspend_disable_irqs();    BUG_ON(!irqs_disabled());    error = syscore_suspend();    if (!error) {        *wakeup = pm_wakeup_pending();        if (!(suspend_test(TEST_CORE) || *wakeup)) {            error = suspend_ops->enter(state);            events_check_enabled = false;        }        syscore_resume();    }    arch_suspend_enable_irqs();    BUG_ON(irqs_disabled());Enable_cpus:    // 8. 重新启用非启动CPU    enable_nonboot_cpus();Platform_wake:    // 9. 平台唤醒通知    if (need_suspend_ops(state) && suspend_ops->wake)        suspend_ops->wake();    dpm_resume_start(PMSG_RESUME);Platform_finish:    // 10. 平台收尾工作    if (need_suspend_ops(state) && suspend_ops->finish)        suspend_ops->finish();    return error;}

0

f1)该接口处理完后,会通过返回值确认是否enter成功,同时通过唤醒指针,通知调用者,是否有唤醒事件发生,导致电源状态切换失败。

        f2)调用suspend_ops的prepare准备(有的话),通知平台代码,杀死让其在即将进行状态切换之时,再做一些处理(需要的话)。该可能失败(平台代码出现意外),失败的话,需要跳至Platform_finish处,调用suspend_ops的finish替代,执行恢复操作。

        f3)调用dpm_suspend_end,调用所有设备的-> suspend_late和-> suspend_noirq变量函数(具体可参考“ Linux电源管理(4)_Power Management Interface ”的描述),暂停后期悬浮设备和需要在关闭中断下的suspend的设备。需要说明的是,这里的noirq,是通过禁止所有的中断线的形式,而不是通过关中断的方式。同样,该操作可能会失败,失败的话,跳到Platform_finish处,执行恢复动作。

        f4)调用suspend_ops的prepare_late替换(有的话),通知平台代码,杀死让其在最后关头,再做一些处理(需要的话)。该可能失败(平台代码出现意外),失败的话,需要跳至Platform_wake处,调用suspend_ops的唤醒,执行设备的恢复,调用suspend_ops的完成,执行恢复操作。

        f5)如果是暂停冻结,执行相应的操作,包括冻结进程,已暂停的设备(参数为PM_SUSPEND_FREEZE),cpu进入空闲状态。如果有任何事件使CPU从空闲状态退出,跳至平台_唤醒位置,执行唤醒操作。

        f6)调用disable_nonboot_cpus,禁止所有的非boot cpu。也会失败,执行恢复操作即可。

        f7)调用arch_suspend_disable_irqs,关闭中断。如果无法关闭,则为bug。

        f8)调用syscore_suspend,挂起系统核心。同样会失败,执行恢复操作即可。有关syscore,我会在另一篇文章中详细描述。

        f9)如果很幸运,以上操作都成功了,那么,切换吧。不过,别高兴太早,还得调用pm_wakeup_pending检查一下,这段时间,是否有唤醒事件发生,如果有就要终止的暂停。

        f10)如果一切顺利,调用suspend_ops的enter变量,进行状态切换。这时,系统应该已经suspend了……

        f11)suspend过程中,唤醒事件发生,系统唤醒,该函数接着执行resume动作,并最终返回。resume动作基本上是suspend的反动作,就不再继续分析了。

        f12)或者,由于意外,暂停终止,该函数也会返回。

g)suspend_enter返回,如果返回原因不是发生错误,而且不是唤醒事件。则调用suspend_ops的suspend_again可以,检查是否需要再次暂停。再什么情况下要再次suspend呢?需要看具体的平台了,谁知道呢。

h)继续恢复操作,恢复设备,启动ftrace,恢复控制台,suspend_ops-> end等。

i)该函数返回后,表示系统已经恢复。 

4.5suspend_finish

比较简单:​​​​​​​

static void suspend_finish(void){    // 1. 解冻被挂起的进程    suspend_thaw_processes();    // 2. 发送挂起结束通知    pm_notifier_call_chain(PM_POST_SUSPEND);    // 3. 恢复控制台    pm_restore_console();}

a)恢复所有的用户空间进程和内核线程。

b)发送暂停结束的通知。

c)将console切换回原来的。 

5.重要知识点回顾

5.1 VT开关

通常情况下,系统控制台模块(driver \ tty \ vt \)会在暂停的过程中,重新分配一个控制台,将控制台切换到该控制台上。然后在恢复时,切换回旧的控制台。就是VT switch功能。VT开关是很耗时的,因此内核提供了一些机制,控制是否使用这个功能:

1)提供一个接口函数pm_set_vt_switch(drivers \ tty \ vt \ vt_ioctl.c),方便其他内核模块从整体上关闭或开启VT switch功能。

2)VT开关VT开关处于开启状态时,满足以下的一种条件(可参考kernel \ power \ console.c相关的描述),即称为能VT开关

        一)有控制台驱动程序调用pm_vt_switch_required接口,显式的要求使能VT开关。PM核心的控制台模块会把这些信息记录在一个名称为pm_vt_switch_list的链表中。

       b)系统禁止在suspend的过程中暂停控制台(由kernel / printk.c中的console_suspend_enabled变量控制)。很有可能需要使用console查看suspend过程,此时为了使console不混乱,有必要进行VT switch。

       c)没有任何控制台驾驶员关心是否需要VT开关,换句话说没有任何驾驶员调用pm_vt_switch_required接口要求使能或禁止VT开关功能。此时会遵循旧的习惯,进行VT开关。  

因此,悬浮过程对console的处理分为4步:

准备控制台:负责在需要VT swich时,将当前控制台切换到SUSPEND控制台。​​​​​​​

int pm_prepare_console(void){    // 1. 检查是否需要虚拟终端切换    if (!pm_vt_switch())        return 0;    // 2. 切换到挂起控制台    orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);    if (orig_fgconsole < 0)        return 1;    // 3. 重定向内核消息    orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);    return 0;}

悬浮式控制台:挂起控制台,由内核/printk.c实现,主要是保持住控制台用的互斥锁,使他人无法使用控制台。  

简历控制台:对控制台解锁。

restore console:将console恢复为初始的console。​​​​​​​

void pm_restore_console(void){    // 1. 检查是否需要虚拟终端切换    if (!pm_vt_switch())        return;    // 2. 恢复原始控制台设置    if (orig_fgconsole >= 0) {        vt_move_to_console(orig_fgconsole, 0);        vt_kmsg_redirect(orig_kmsg);    }}

5.2冻结任务

进度的冻结功能,是暂停,休眠,等电源管理功能的组成部分,在新版本内核中,它被独立出来,作为一个独立的电源管理状态(冻结)。该功能的目的,是在电源管理的状态切换过程中,确保所有用户空间进程和部分内核线程处于一个稳定的状态。

5.3 PM通知者

PM notifier是基于内核的blocking notifier功能实现的。。block notifier提供了一种内核内部的消息通知机制,消息接受者通过notifier注册的方式,注册一个较小的函数,关注消息发送者发出的notifier。当消息产生时,,消息产生者通过调用称为函数的形式,通知消息接受者。这种调用,是可以被分割的,因此阻止了通知程序。

那suspend功能为什么使用notifier呢?原因可能有多个,这里我举一个例子,这是我们日常开发中可能会遇到的。

由之前的描述可知,暂停过程中,暂停设备发生在进程被冻结之后,恢复设备发生在进程被恢复之前。

1)如果有些设备就需要在冻结进度之前暂停如何办?

2)如果某些设备的恢复动作需要连续重复,或者要等待什么事情发生,那么如果它的恢复动作发生在进程恢复之前,岂不是要阻止所有进程的恢复?更甚者,如果该设备要等待某个进程的数据才能恢复,怎么办?

再来看suspend_prepare和suspend_finish中的处理:​​​​​​​

static int suspend_prepare(suspend_state_t state){    int error;    // 1. 调用挂起准备通知链    error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);    if (error)        goto Finish;    // 2. 冻结用户进程    error = suspend_freeze_processes();    if (!error)        return 0;    // 3. 错误处理    suspend_stats.failed_freeze++;    dpm_save_failed_step(SUSPEND_FREEZE);Finish:    // 4. 清理工作    pm_notifier_call_chain(PM_POST_SUSPEND);    pm_restore_console();    return error;}static void suspend_finish(void){    // 1. 解冻用户进程    suspend_thaw_processes();    // 2. 调用挂起完成通知    pm_notifier_call_chain(PM_POST_SUSPEND);    // 3. 恢复控制台    pm_restore_console();}

原来PM notifier是在设备模型的框架外,开了一个后门,那些比较特殊的驱动程序,可以绕过设备模型,直接接收PM发送的暂停信息,杀死执行自身的暂停动作。特别是resume时,可以在其他进展都正好工作的时候,只让暂停进展

感兴趣的读者,可以围观一下下面这个活生生的例子(顺便提一下,好的设计是不应该有例外的):

驱动程序\视频\ omap2 \ dss \ core.c  

5.4设备PM ops和平台PM ops的调用时机

对Linux的驱动工程师来说,设备PM OPS和平台PM OPS就是电源管理(暂停)的全部,只要在合适的地方,实现合适的回调函数,即可实现系统的电源管理。但现实太复杂了,以如kernel提供的这两个数据结构也很复杂,再回忆一下,如下:

​​​​​​​

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);    /* noirq(无中断上下文)回调 */    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);};struct platform_suspend_ops {    /* 状态验证 */    int (*valid)(suspend_state_t state);    /* 挂起流程控制 */    int (*begin)(suspend_state_t state);    int (*prepare)(void);    int (*prepare_late)(void);    int (*enter)(suspend_state_t state);    /* 唤醒流程控制 */    void (*wake)(void);    void (*finish)(void);    /* 高级控制 */    bool (*suspend_again)(void);    void (*end)(void);    void (*recover)(void);};

虽然内核的注释已经相当详细了,但我们一定会犯晕,到底该实现什么地方?这些替代的应用场景又是什么?蜗蜗以为,要熟练使用这些替代,唯一的方法就是多编码,多理解。,另外,我们可以总结一下在电源状态切换时,这些替代的调用时机,从侧面帮助理解。如下(只介绍和suspend功能有关的,struct dev_pm_ops简称D,struct platform_suspend_ops简称P):

0

5.5暂停过程的同步和PM唤醒

最重要的事情,如果暂停的过程中,有唤醒事件产生怎么办?正常的流程,应该终止暂停,返回并处理事件。但由于暂停而发生的特殊性,进程被冻结,关断等等,导致事情并没有那么简单,以至于在很久的一段时间内,内核都不能很好的处理。这也称作挂起过程的同步问题。

在美好的旧时光里,suspend主要是由于热关机,因此同步问题的影响并不突出(因为操作并不重复)。但来到新时代之后,事情变了,Android竟然用suspend作日常的暂停(那怎么解决呢?得靠系统唤醒框架,也就是suspend过程中所调用的pm_wakeup_pending接口所在的模块。我会在下一篇文章中继续该模块的分析,这里就不再继续了。

http://www.xdnf.cn/news/3971.html

相关文章:

  • Ubuntu K8S(1.28.2) 节点/etc/kubernetes/manifests 不存在
  • n8n工作流自动化平台:生成图文并茂的分析报告之Merge节点详细说明
  • labelimg快捷键
  • DXFViewer进行中 : ->封装OpenGL -> 解析DXF直线
  • SpringMVC框架详解与实践指南
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】4.3 数据脱敏与安全(模糊处理/掩码技术)
  • 力扣119题解
  • 六、shell脚本--正则表达式:玩转文本匹配的“万能钥匙”
  • Java使用JDBC操作数据库
  • OpenCV进阶操作:图像直方图、直方图均衡化
  • 2.CFD 计算过程概述:Fluent在散热计算中的优势
  • 【Linux】linux入门——基本指令
  • Qt 信号槽机制底层原理学习
  • C++笔记之模板与可变参数模板
  • 动态链接库(DLL)
  • 网狐飞云娱乐三端源码深度实测:组件结构拆解与部署Bug复盘指南(附代码分析)
  • LeetCode 热题 100 17. 电话号码的字母组合
  • 分布式事物
  • VTK 系统架构
  • 【NLP】33. Pinecone + OpenAI :构建自定义语义搜索系统
  • 五一作业-day04
  • 解决跨域的4种方法
  • CRS 16 slot 设备硬件架构
  • 【RK3588嵌入式图形编程】-Cairo-Cairo图形库支持后端
  • 嵌入式Linux驱动学习
  • 哈希算法、搜索算法与二分查找算法在 C# 中的实现与应用
  • 基于机器学习算法预测二手车市场数据清洗与分析平台(源码+定制+讲解) 基于Python的数据挖掘与可视化 二手车数据处理与分析系统开发 (机器学习算法预测)
  • 深入理解 Bash 中的 $‘...‘ 字符串语法糖
  • 浅拷贝和深拷贝的区别
  • Android控件View、ImageView、WebView用法