当前位置:首页>Linux>Linux 自动休眠唤醒刨析

Linux 自动休眠唤醒刨析

  • 2026-10-21 13:29:07
Linux 自动休眠唤醒刨析


文章目录

  • 一、autosleep
  • 二、/sys/power/autosleep
  • 三、pm_autosleep_init
  • 四、pm_autosleep_state()
  • 五、try_to_suspend()

沉淀、分享、成长,让自己和他人都能有所收获!😄

Linux 驱动课程火热更新中:放大招!瑞芯微底层驱动课程~

一、autosleep


autosleep 基于wakeup source实现, 用于取代android wakelock中的自动休眠功能。
autosleep实现代码位于“kernel/power/autosleep.c”。
编译内核时, 必须配置CONFIG_PM_AUTOSLEEP选项才能enable autosleep。

二、/sys/power/autosleep


autosleep在sysfs中提供了一个属性文件“/sys/power/autosleep”用以配置autosleep, 类似于“/sys/power/state”文件可以控制suspend到不同的状态, autosleep也可配置不同的状态:

  1. freeze : auto freeze
  2. standby : auto standby
  3. mem : auto mem
  4. disk : auto disk
  5. off : disable autosleep

向“/sys/power/autosleep”写入以上5个值的任意一个可将autosleep切换到指定的状态,一旦写入非“off”值, autosleep就开始运行
读取“/sys/power/autosleep”可以获得当前的状态, 特别的, 如果系统不支持autosleep, 读取该文件会输出”error”
“/sys/power/autosleep”的show()/store()方法由pm_autosleep_state()/pm_autosleep_set_state()来实现,在下面会详细解释

三、pm_autosleep_init


autosleep的初始化函数pm_autosleep_init()在PM的初始化时(kernel/power/main.c:pm_init)被调用:

int __init pm_autosleep_init(void)
{
        autosleep_ws =wakeup_source_register("autosleep");
if(!autosleep_ws)
return-ENOMEM;

        autosleep_wq =alloc_ordered_workqueue("autosleep",0);
if(autosleep_wq)
return0;

wakeup_source_unregister(autosleep_ws);
return-ENOMEM;
}
  1. 注册一个名为“autosleep”的wakeup source, 用于在autosleep执行关键代码执行时阻止系统休眠
  2. 分配一个名为“autosleep”的有序workqueue用于调度工作, 触发休眠
    autosleep中定义了一个work:
staticDECLARE_WORK(suspend_work, try_to_suspend);

voidqueue_up_suspend_work(void)
{
if(autosleep_state > PM_SUSPEND_ON)
queue_work(autosleep_wq,&suspend_work);
}

可以看到, queue_up_suspend_work()中会调度work, 运行try_to_suspend()来触发休眠, 那么什么时候会调用queue_up_suspend_work()呢? 是在“/sys/power/autosleep”的store()方法pm_autosleep_state()中调用的

四、pm_autosleep_state()


pm_autosleep_state()的代码比较简单

intpm_autosleep_set_state(suspend_state_t state)
{

#ifndefCONFIG_HIBERNATION
if(state >= PM_SUSPEND_MAX)
return-EINVAL;
#endif

__pm_stay_awake(autosleep_ws);

mutex_lock(&autosleep_lock);

        autosleep_state = state;

__pm_relax(autosleep_ws);

if(state > PM_SUSPEND_ON){
pm_wakep_autosleep_enabled(true);
queue_up_suspend_work();
}else{
pm_wakep_autosleep_enabled(false);
}

mutex_unlock(&autosleep_lock);
return0;
}
  • 首先使用_pm_stay_awake()/__pm_relax()操纵名为“autosleep”的wakelock, 在其中修改autosleep_state为向”/sys/power/autosleep”写入的值, (读取”/sys/power/autosleep”会返回autosleep_state)
  • 若是写入“/sys/power/autosleep”的值不为“off”,则调用pm_wakep_autosleep_enabled(true), 然后调用queue_up_suspend_work()触发工作队列“autosleep”开始运行
  • 若是写入“/sys/power/autosleep”的值为“off”,则调用pm_wakep_autosleep_enabled(true)
    pm_wakep_autosleep_enabled()主要用于更新所有wakeup source的autosleep_enabled标志(为何不使用全局的autosleep_enabled标记?), 对于为active状态的wakeup source,说明它在阻止autosleep, 若此时参数为true, 则更新wakeup source的start_prevent_time(开始组织suspend的时间), 若参数为false,则更新wakeup source的prevent_sleep_time(总共阻止睡眠的时间)

五、try_to_suspend()


staticvoidtry_to_suspend(structwork_struct*work)
{
unsignedint initial_count, final_count;

if(!pm_get_wakeup_count(&initial_count, true))
goto out;

mutex_lock(&autosleep_lock);

if(!pm_save_wakeup_count(initial_count)||
                system_state != SYSTEM_RUNNING){
mutex_unlock(&autosleep_lock);
goto out;
}

if(autosleep_state == PM_SUSPEND_ON){
mutex_unlock(&autosleep_lock);
return;
}
if(autosleep_state >= PM_SUSPEND_MAX)
hibernate();
else
pm_suspend(autosleep_state);

mutex_unlock(&autosleep_lock);

if(!pm_get_wakeup_count(&final_count, false))
goto out;

if(final_count == initial_count)
schedule_timeout_uninterruptible(HZ /2);
out:
queue_up_suspend_work();
}
  • 该接口是wakeup count的一个例子, 如wakeup count中所描述, 调用pm_get_wakeup_count(block为true),阻塞等待, 直到没有wakeup event在处理才返回count, 如果读取失败, 则调度workqueue, 重新进入try_to_suspend()
  • 将获取wakeup count,保存在initial_count中
  • 将initial_count写入saved_count, 如果失败, 说明产生了wakeup event, 则则调度workqueue, 重新进入try_to_suspend()
  • 如果成功,且当前系统状态为running,根据autosleep状态,调用hibernate或者pm_suspend,suspend系统
  • 运行到mutex_unlock(&autosleep_lock)这一行时,说明是suspend过程中遇到了wakeup event, 或者是suspend之后被唤醒
  • 非阻塞情况下在读取wakeup count, 保存到final count中, 如果此时有wakeup event在处理, 则调度workqueue, 重新进入try_to_suspend()
  • 如果initial_count和final count相同, 说明suspend之间和resume之后的wakeup count相同, 比较奇怪, 需要等待0.5s后再尝试则调度workqueue, 重新进入try_to_suspend()
    在queue_up_suspend_work()中运行try_to_suspend(), try_to_suspend()中又调用queue_up_suspend_work(), 这样就形成了一个环, 尝试suspend, resume后又继续尝试suspend, 只要通过“/sys/power/autosleep”enable autosleep之后, 这个环就开始运行, 那么何时停止autosleep呢, 那就是向“/sys/power/autosleep”写如”off”后, 在try_to_suspend()中判断到autosleep_state == PM_SUSPEND_ON时, 直接返回, 不再调用queue_up_suspend_work(), 这个环就断开了, autosleep也就停止了
    autosleep的源码很少, 只有100行左右, 可自行阅读

最新文章

随机文章