热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Android休眠唤醒机制

有四种方式可以引起休眠①在wake_unlock()中,如果发现解锁以后没有任何其他的wakelock了,就开始休眠②在定时器到时间以后,定时器的回调函

有四种方式可以引起休眠

        ①在wake_unlock()中, 如果发现解锁以后没有任何其他的wake lock了, 就开始休眠

        ②在定时器到时间以后, 定时器的回调函数会查看是否有其他的wake lock, 如果没有, 就在这里让系统进入睡眠

        ③在wake_lock() 中, 对一个wake lock加锁以后, 会再次检查一下有没有锁,  刚加上锁,为什么要检查,有用吗?

        ④按power键,调用earlysuspend.使系统或应用程序释放锁.从而调用上述三个函数进入休眠

  1. earlysuspend、autosleep以及wakeup_count三种休眠机制的分析和比较

1) android 面临的问题

Opportunistic sleep: 当没有任务时,需要寻找时机,进入suspended

2) 3类同步问题

a. 内核:driver处理event的过程中,系统不能suspend

b. 用户:用户进程处理的过程中系统不能suspend

c. 内核与用户交互:

休眠过程中,触发event, 需abort suspend流程;

event 事件需用户态处理完毕后,才能suspend;

2.常见的休眠唤醒机制有三种:earlysuspend、autosleep、wakeup_count。

  1. earlysuspend机制介绍:

    由上图可得,应用程序application层创建,申请,释放wake_lock锁,通过上层应用类PowerManagerService.java获得和释放wake_lock锁。

    对于进入休眠,有两种情况:

    1. 一种是power键灭屏关闭LCD、TP,或者后台有一些进程正在执行,如音乐播放器、文件传输之类,此时系统会先进入early_suspend状态,然后当这些正在执行的进程执行完成,PowerManagerService检查系统已经没有wake_lock锁时,系统进入深度休眠状态,Early_suspend与late_resume成对出现。
    2. 另一种是,系统直接检查到没有wake_lock锁时,直接进入深度睡眠状态。

       

  2. Autosleep

  3. Wakeup_count

 

  1. Android在Linux内核原有的睡眠唤醒模块基础上,主要增加了下面三个机制

    • Wake Lock 唤醒锁机制;

    • Early Suspend 预挂起机制;

    • Late Resume 迟唤醒机制;

    其基本原理:当启动一个应用程序的时候,它可以申请一个wake_lock唤醒锁,每当申请成功后都会在内核中注册一下(通知系统内核,现在已经有锁被申请,系统内核的wake_lock_store把它加入锁中),当应用程序在某种情况下释放wake_lock的时候,会注销之前所申请的wake_lock。特别要注意的是:只要是系统中有一个wake_lock的时候,系统此时都不能进行睡眠。但此时各个模块可以进行early_suspend。当系统中所有的wake_lock都被释放之后,系统就会进入真正的kernel的睡眠状态。在系统启动的时候会创建一个主唤醒锁main_wake_lock,该锁是内核初始化并持有的一个WAKE_LOCK_SUSPEND属性的非限时唤醒锁。因此,系统正常工作时,将始终因为该锁被内核持有而无法进入睡眠状态。也就是说在不添加新锁的情况下,只需将main_wake_lock 解锁,系统即可进入睡眠状态。

    从Android最上层(Java的应用程序),经过Java、C++和C语言写的Framework层、JNI层、HAL层最后到达android的最底层(Kernel层)。

     

    相关代码位置:

    Frameworks

    // 供给上层应用程序调用的接口

    frameworks/base/core/java/android/os/PowerManager.java

    // 具体实现PowerManager类中的接口

    frameworks/base/services/java/com/android/server/PowerManagerService.java

    // 被PowerManagerService类调用

    frameworks/base/core/java/android/os/ Power.java

    • JNI

    // 实现Power类中的JNI接口

    frameworks/base/core/jni/android_os_Power.cpp

    • HAL

    // 进行sysfs用户接口的操作

    hardware/libhardware_legacy/power/power.c

    • Kernel

    kernel/kernel/power/main.c

    kernel/kernel/power/earlysuspend.c

    kernel/kernel/power/suspend.c

    kernel/kernel/power/wakelock.c

    kernel/kernel/power/userwakelock.c

    在应用程序框架层中,PowerManager类是面向上层应用程序的接口类,提供了Wake Lock机制(同时也是睡眠唤醒子系统)的基本接口(唤醒锁的获取和释放)。上层应用程序通过调用这些接口,实现对系统电源状态的监控。

    • PowerManager类通过IBinder这种Android中特有的通信模式,与PowerManagerService 类进行通信。

    PowerManagerService 是PowerManager 类中定义的接口的具体实现,并进一步调用Power 类来与下一层进行通信。PowerManagerService 类是WakeLock 机制在应用程序框架层的核心,他们对应用程调用PowerManager类接口时所传递的参数进行初步的分析和对应的设置,并管理一个唤醒锁队列,然后配合其他模块(例如WatchDog、BatteryService、ShutdownThread 等)的状态信息,做出决策,调用Power类的对应接口,最终通过JNI 接口,调用到硬件抽象层中的函数,对sysfs 的用户接口进行操作,从而触发内核态实现的功能。

  2. 获得wake_lock唤醒锁

    比如在应用程序中,当获得wakelock唤醒锁的时候,它首先调用/android/frameworks/base/core/java/android/os/PowerManager类中的public void acquire()办法,而此方法通过Binder将调用PowerManagerService类中的public void acquireWakeLock。

    在用户态的调用流程如下:

    上述write实质上是文件sysfs: /sys/power/wake_lock,当write时,它将调用userwakelock.c::wake_lock_store()函数,其实现如下:

    1. 根据name在红黑树中查找user_wake_lock,若找到则直接返回;否则为它分配内存、调用wake_lock_init初始化、然后增加到红黑树中。

    2. 调用wake_lock或wake_lock_timeout,它将调用wake_lock_internal

     

    wake_lock_internal()函数流程:

    1) 判断锁的类型是否有效,即是否为WAKE_LOCK_SUSPEND或WAKE_LOCK_IDLE某一种 

    2) 如果定义了CONFIG_WAKELOCK_STAT, 则更新struct wake_lock里面的用于统计锁信息的成员变量 

    3) 将锁从inactive_locks链表上取下,加到active_wake_locks链表上。如果是超期锁则设置锁的flag|=WAKE_LOCK_AUTO_EXPIRE,否则取消WAKE_LOCK_AUTO_EXPIRE标志。如果锁是WAKE_LOCK_SUSPEND型的,则继续下面的步骤。 

    4) 对于WAKE_LOCK_SUSPEND型的锁如果它是超期锁,则调用has_wake_lock_locked函数检查所有处于活动状态的WAKE_LOCK_SUSPEND锁(即在active_wake_locks链表上的WAKE_LOCK_SUSPEND锁,或者说当前被加锁了的WAKE_LOCK_SUSPEND锁),是否有超期锁已经过期,如果有则把过期超期锁从active_wake_locks上删除,挂到inactive_locks上。同时它还检查链表上有没有非超期锁,如果有则直接返回-1,否则它最终返回的是所有超期锁过期时间的最大值 

    5) 如果has_wake_lock_locked函数返回的是-1(表示当前活动锁有非超时锁)或者0(表示所有活动锁都是超时锁,且全已经超时),则删除expire_timer,并排队一个suspend工作到suspend_work_queue工作队列,最终系统会suspend。suspend函数完成suspend系统的任务,它是suspend_work这个工作的处理函数,suspend_workk排队到suspend_work_queue工作队列中,最终系统会处理这个work,调用其handler即suspend函数。该函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用 enter_state()来进入 linux的suspend流程。

     

     

  3. 系统进入休眠(suspend)

    当按了MID上的power键,经过一系统列的事务处理后,它会调用到PowerManager类中的goToSleep。在用户态的调用流程如下所示:

    最后一步write()写操作把"mem"写入/sys/power/state中。接下来,在kernel态,state_store函数将被调用。

     

    • Android特有的earlysuspend: request_suspend_state(state)

    • Linux标准的suspend: enter_state(state)

     

    在state_store()首先判断用户写入的是否是"disk"字符串,如果是则调用hibernate()函数命令系统进入hibernation状态。如果是其他字符串则调用request_suspend_state()(如果定义 CONFIG_EARLYSUSPEND)或者调用enter_state()(如果未定义CONFIG_EARLYSUSPEND)。 request_suspend_state()函数是android相对标准linux改动的地方,它实现在earlysuspend.c中。在标准linux内核中,用户通过 sysfs 写入"mem"和"standby"时,会直接调用enter_state()进入suspend模式,但在android中则会调用request_suspend_state()函数进入early suspend状态。

static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,

const char *buf, size_t n)

{

#ifdef CONFIG_SUSPEND

#ifdef CONFIG_EARLYSUSPEND

suspend_state_t state = PM_SUSPEND_ON;

#else

suspend_state_t state = PM_SUSPEND_STANDBY;

#endif

const char * const *s;

#endif

char *p;

int len;

int error = -EINVAL;

   

p = memchr(buf, '\n', n);

len = p ? p - buf : n;

   

   

if (len == 4 && !strncmp(buf, "disk", len)) {

error = hibernate();

goto Exit;

}

   

#ifdef CONFIG_SUSPEND

for (s = &pm_states[state]; state

if (*s && len == strlen(*s) && !strncmp(buf, *s, len))

break;

}

if (state

#ifdef CONFIG_EARLYSUSPEND

if (state == PM_SUSPEND_ON || valid_state(state)) {

error = 0;

request_suspend_state(state);

}

#else

error = enter_state(state);

#endif

#endif

Exit:

return error ? error : n;

}

在request_suspend_state(state)函数中,会调用early_suspend_work的工作队列,从而进入early_suspend()函数中。

static DECLARE_WORK(early_suspend_work, early_suspend);

void request_suspend_state(suspend_state_t new_state)

{

unsigned long irqflags;

int old_sleep;

   

spin_lock_irqsave(&state_lock, irqflags);

old_sleep = state & SUSPEND_REQUESTED;

if (debug_mask & DEBUG_USER_STATE) {

struct timespec ts;

struct rtc_time tm;

getnstimeofday(&ts);

rtc_time_to_tm(ts.tv_sec, &tm);

pr_info("request_suspend_state: %s (%d->%d) at %lld "

"(%d-d-d d:d:d. lu UTC)\n",

new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",

requested_suspend_state, new_state,

ktime_to_ns(ktime_get()),

tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,

tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);

}

if (!old_sleep && new_state != PM_SUSPEND_ON) {

state |= SUSPEND_REQUESTED;

queue_work(suspend_work_queue, &early_suspend_work);

} else if (old_sleep && new_state == PM_SUSPEND_ON) {

state &= ~SUSPEND_REQUESTED;

wake_lock(&main_wake_lock);

queue_work(suspend_work_queue, &late_resume_work);

}

requested_suspend_state = new_state;

spin_unlock_irqrestore(&state_lock, irqflags);

}

early_suspend()函数中,首先要判断当前请求的状态是否还是suspend,若不是,则直接退出了;若是,函数会调用已经注册的early_suspend的函数。然后同步文件系统,最后释放main_wake_lock

static void early_suspend(struct work_struct *work)

{

struct early_suspend *pos;

unsigned long irqflags;

int abort = 0;

   

mutex_lock(&early_suspend_lock);

spin_lock_irqsave(&state_lock, irqflags);

if (state == SUSPEND_REQUESTED)

state |= SUSPENDED;

else

abort = 1;

spin_unlock_irqrestore(&state_lock, irqflags);

   

if (abort) {

if (debug_mask & DEBUG_SUSPEND)

pr_info("early_suspend: abort, state %d\n", state);

mutex_unlock(&early_suspend_lock);

goto abort;

}

   

if (debug_mask & DEBUG_SUSPEND)

pr_info("early_suspend: call handlers\n");

list_for_each_entry(pos, &early_suspend_handlers, link) {

if (pos->suspend != NULL)

pos->suspend(pos);

}

mutex_unlock(&early_suspend_lock);

   

if (debug_mask & DEBUG_SUSPEND)

pr_info("early_suspend: sync\n");

   

sys_sync();

abort:

spin_lock_irqsave(&state_lock, irqflags);

if (state == SUSPEND_REQUESTED_AND_SUSPENDED)

wake_unlock(&main_wake_lock);

spin_unlock_irqrestore(&state_lock, irqflags);

}

wake_unlock()中,删除链表中wake_lock节点,判断当前是否存在wake_lock,若wake_lock的数目为0,则调用工作队列suspend_work,进入suspend状态。

tatic DECLARE_WORK(suspend_work, suspend);

void wake_unlock(struct wake_lock *lock)

{

int type;

unsigned long irqflags;

spin_lock_irqsave(&list_lock, irqflags);

type = lock->flags & WAKE_LOCK_TYPE_MASK;

#ifdef CONFIG_WAKELOCK_STAT

wake_unlock_stat_locked(lock, 0);

#endif

if (debug_mask & DEBUG_WAKE_LOCK)

pr_info("wake_unlock: %s\n", lock->name);

lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);

list_del(&lock->link);

list_add(&lock->link, &inactive_locks);

if (type == WAKE_LOCK_SUSPEND) {

long has_lock = has_wake_lock_locked(type);

if (has_lock > 0) {

if (debug_mask & DEBUG_EXPIRE)

pr_info("wake_unlock: %s, start expire timer, "

"%ld\n", lock->name, has_lock);

mod_timer(&expire_timer, jiffies + has_lock);

} else {

if (del_timer(&expire_timer))

if (debug_mask & DEBUG_EXPIRE)

pr_info("wake_unlock: %s, stop expire "

"timer\n", lock->name);

if (has_lock == 0)

queue_work(suspend_work_queue, &suspend_work);

}

if (lock == &main_wake_lock) {

if (debug_mask & DEBUG_SUSPEND)

print_active_locks(WAKE_LOCK_SUSPEND);

#ifdef CONFIG_WAKELOCK_STAT

update_sleep_wait_stats_locked(0);

#endif

}

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

在suspend()函数中,先判断当前是否有wake_lock,若有,则退出;然后同步文件系统,最后调用pm_suspend()函数。

static void suspend(struct work_struct *work)

{

int ret;

int entry_event_num;

   

if (has_wake_lock(WAKE_LOCK_SUSPEND)) {

if (debug_mask & DEBUG_SUSPEND)

pr_info("suspend: abort suspend\n");

return;

}

   

entry_event_num = current_event_num;

sys_sync();

if (debug_mask & DEBUG_SUSPEND)

pr_info("suspend: enter suspend\n");

ret = pm_suspend(requested_suspend_state);

if (debug_mask & DEBUG_EXIT_SUSPEND) {

struct timespec ts;

struct rtc_time tm;

getnstimeofday(&ts);

rtc_time_to_tm(ts.tv_sec, &tm);

pr_info("suspend: exit suspend, ret = %d "

"(%d-d-d d:d:d. lu UTC)\n", ret,

tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,

tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);

}

if (current_event_num == entry_event_num) {

if (debug_mask & DEBUG_SUSPEND)

pr_info("suspend: pm_suspend returned with no event\n");

wake_lock_timeout(&unknown_wakeup, HZ / 2);

}

}

在pm_suspend()函数中,enter_state()函数被调用,从而进入标准linux休眠过程

int pm_suspend(suspend_state_t state)

{

if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX)

return enter_state(state);

return -EINVAL;

}

在enter_state()函数中,首先检查一些状态参数,再同步文件系统,然后调用suspend_prepare()来冻结进程,最后调用suspend_devices_and_enter()让外设进入休眠。

static int enter_state(suspend_state_t state)

{

int error;

   

if (!valid_state(state))

return -ENODEV;

   

if (!mutex_trylock(&pm_mutex))

return -EBUSY;

   

printk(KERN_INFO "PM: Syncing filesystems ... ");

sys_sync();

printk("done.\n");

   

pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);

error = suspend_prepare();

if (error)

goto Unlock;

   

if (suspend_test(TEST_FREEZER))

goto Finish;

   

pr_debug("PM: Entering %s sleep\n", pm_states[state]);

error = suspend_devices_and_enter(state);

   

Finish:

pr_debug("PM: Finishing wakeup.\n");

suspend_finish();

Unlock:

mutex_unlock(&pm_mutex);

return error;

}

外部设备在注册时,都会注册其自身的suspend函数,enter_state()函数通过回调这些设备的suspend使其进入suspend态

 

  1. Linux电源管理的基础培训

     

    1. 高通平台的电源管理机制是由RPM(running PowerManager)统一管理,每个子系统都有自己的休眠唤醒机制,当子系统进入待机时,其会发送消息给RPM,并让自身进入待机状态,RPM子系统根据各子系统的休眠状态,决定整个系统是否进待机(XO_SHUTDOWN)
    2. PMIC有单独的电源给子系统供电,这就决定了子系统待机时可以关闭自身的电源用于降低功耗。
    3. AP的待机有几种模式:
      1. WFI:wait for interrupt:正常的待机状态,AP电源关闭,GIC模块工作,用于接收中断
      2. Idle power collapse: 正常工作时CPU进低功耗模式
    4. 子系统的待机不受其他子系统的影响
  2. AP休眠

    AP的休眠唤醒流程由应用层发起,统一由power manager service线程管理,其通过JNI调用libsuspend的库函数,对内核进行操作。 Powermanger service: /frameworks/base/service/core/java/com/android/server/power/powermanagerservice.java JNI: /frameworks/base/service/core/jni Libsuspend: /system/core/libsuspend

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

     

    内核的接口 Linux内核有两种休眠唤醒接口: /sys/power/autosleep /sys/power/state 其区别在autosleep的方式为当应用设置系统休眠时,其如果发现无法进待机,则会不断的尝试进行待机。State方式为用户空间设置系统待机,如果无法待机,则退出待机流程。 Autosleep方式目前默认不采用,主要原因是应用的控制流程上有无法规避的问题。

     

  3. 每个模块都会注册其自身的休眠唤醒流程,用于进行低功耗处理

对于系统核心模块的休眠是在最后一步syscore suspend中完成,如cpu、GIC、CLK、SMD的休眠处理。


推荐阅读
  • 本文详细介绍了Java中实现异步调用的多种方式,包括线程创建、Future接口、CompletableFuture类以及Spring框架的@Async注解。通过代码示例和深入解析,帮助读者理解并掌握这些技术。 ... [详细]
  • 目录一、salt-job管理#job存放数据目录#缓存时间设置#Others二、returns模块配置job数据入库#配置returns返回值信息#mysql安全设置#创建模块相关 ... [详细]
  • 我有一个SpringRestController,它处理API调用的版本1。继承在SpringRestControllerpackagerest.v1;RestCon ... [详细]
  • 使用Nginx反向代理实现多域名端口映射
    本文介绍如何通过配置本地hosts文件和Nginx反向代理,实现多个虚拟域名的端口映射,使用户可以通过标准HTTP端口80访问不同后端服务。 ... [详细]
  • Linux环境下C语言实现定时向文件写入当前时间
    本文介绍如何在Linux系统中使用C语言编程,实现在每秒钟向指定文件中写入当前时间戳。通过此示例,读者可以了解基本的文件操作、时间处理以及循环控制。 ... [详细]
  • 深入解析SpringMVC核心组件:DispatcherServlet的工作原理
    本文详细探讨了SpringMVC的核心组件——DispatcherServlet的运作机制,旨在帮助有一定Java和Spring基础的开发人员理解HTTP请求是如何被映射到Controller并执行的。文章将解答以下问题:1. HTTP请求如何映射到Controller;2. Controller是如何被执行的。 ... [详细]
  • 通常情况下,修改my.cnf配置文件后需要重启MySQL服务才能使新参数生效。然而,通过特定命令可以在不重启服务的情况下实现配置的即时更新。本文将详细介绍如何在线调整MySQL配置,并验证其有效性。 ... [详细]
  • 深入解析 Android IPC 中的 Messenger 机制
    本文详细介绍了 Android 中基于消息传递的进程间通信(IPC)机制——Messenger。通过实例和源码分析,帮助开发者更好地理解和使用这一高效的通信工具。 ... [详细]
  • Linux环境下进程间通信:深入解析信号机制
    本文详细探讨了Linux系统中信号的生命周期,从信号生成到处理函数执行完毕的全过程,并介绍了信号编程中的注意事项和常见应用实例。通过分析信号在进程中的注册、注销及处理过程,帮助读者理解如何高效利用信号进行进程间通信。 ... [详细]
  • 对于许多初学者而言,遇到总线错误(bus error)或段错误(segmentation fault/core dump)是极其令人困扰的。本文详细探讨了这两种错误的成因、表现形式及解决方法,并提供了实用的调试技巧。 ... [详细]
  • 主调|大侠_重温C++ ... [详细]
  • ListView简单使用
    先上效果:主要实现了Listview的绑定和点击事件。项目资源结构如下:先创建一个动物类,用来装载数据:Animal类如下:packagecom.example.simplelis ... [详细]
  • 在编译BSP包过程中,遇到了一个与 'gets' 函数相关的编译错误。该问题通常发生在较新的编译环境中,由于 'gets' 函数已被弃用并视为安全漏洞。本文将详细介绍如何通过修改源代码和配置文件来解决这一问题。 ... [详细]
  • 本文详细介绍了如何在Android 4.4及以上版本中配置WebView以实现内容的自动高度调整和屏幕适配,确保中文显示正常,并提供代码示例。 ... [详细]
  • 本文将详细介绍如何在没有显示器的情况下,使用Raspberry Pi Imager为树莓派4B安装操作系统,并进行基本配置,包括设置SSH、WiFi连接以及更新软件源。 ... [详细]
author-avatar
泉州多棱汽车销售服务有限公司
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有