码迷,mamicode.com
首页 > 移动开发 > 详细

Android休眠唤醒机制

时间:2017-12-21 11:53:26      阅读:734      评论:0      收藏:0      [点我收藏+]

标签:地方   handlers   pmi   hand   发送   exp   内存   attribute   方式   

有四种方式可以引起休眠

        ①在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 < PM_SUSPEND_MAX; s++, state++) {

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

break;

}

if (state < PM_SUSPEND_MAX && *s)

#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的休眠处理。 技术分享图片

Android休眠唤醒机制

标签:地方   handlers   pmi   hand   发送   exp   内存   attribute   方式   

原文地址:http://www.cnblogs.com/yejintianming00/p/8078596.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!