有四种方式可以引起休眠

①在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);

}

,则调用工作队列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休眠唤醒机制的更多相关文章

  1. Android休眠唤醒机制简介(二)

    本文转载自:http://blog.csdn.net/zhaoxiaoqiang10_/article/details/24408911 Android休眠唤醒机制简介(二)************* ...

  2. Android休眠唤醒机制简介(一)【转】

    本文转载自:http://blog.csdn.net/zhaoxiaoqiang10_/article/details/24408129 Android休眠唤醒机制简介(一) ************ ...

  3. android 休眠唤醒机制分析(二) — early_suspend

    本文转自:http://blog.csdn.net/g_salamander/article/details/7982170 early_suspend是Android休眠流程的第一阶段即浅度休眠,不 ...

  4. android 休眠唤醒机制分析(一) — wake_lock

    本文转自:http://blog.csdn.net/g_salamander/article/details/7978772 Android的休眠唤醒主要基于wake_lock机制,只要系统中存在任一 ...

  5. android 休眠唤醒机制分析(一) — wake_lock【转】

    Android的休眠唤醒主要基于wake_lock机制,只要系统中存在任一有效的wake_lock,系统就不能进入深度休眠,但可以进行设备的浅度休眠操作.wake_lock一般在关闭lcd.tp但系统 ...

  6. android 休眠唤醒机制分析(三) — suspend

    本文转自:http://blog.csdn.net/g_salamander/article/details/7988340 前面我们分析了休眠的第一个阶段即浅度休眠,现在我们继续看休眠的第二个阶段 ...

  7. android休眠唤醒驱动流程分析【转】

    转自:http://blog.csdn.net/hanmengaidudu/article/details/11777501 标准linux休眠过程: l        power managemen ...

  8. Android睡眠唤醒机制--Kernel态

    一.简介 Android系统中定义了几种低功耗状态:earlysuspend.suspend.hibernation.       1) earlysuspend: 是一种低功耗的状态,某些设备可以选 ...

  9. android休眠唤醒流程2

    android系统一段时间没有操作,    屏幕(screen)将从高亮(bright)变为暗淡(dim),如果再过段时间还是没有操作,屏幕(screen)从暗淡(dim)变为关闭(off).这时,系 ...

随机推荐

  1. 【设计思路】Booking优化

    https://www.uisdc.com/booking-redesign https://medium.muz.li/booking-com-ux-case-study-7ffb39e54791

  2. LODOP设置同一个任务发送到不同打印机

    前面的博文Lodop打印语句最基本结构介绍(什么是一个任务),一个任务只能有一个打印语句(最后PRINT,PRINTA,PREVIEW等),如果执行多个,LODOP会弹出多次,C-LODOP会提示窗口 ...

  3. Python扫描器-HTTP协议

    1.HTTP协议 1.1.HTTP协议简介 #1.HTTP协议,全称Hyper Text Transfer Protocol(超文本传输协议) HTTP协议是用于从(WWW:World Wide We ...

  4. python爬取网页数据方法

    """#最基本,请求地址无参数# response=urllib.request.urlopen("https://www.scetc.edu.cn" ...

  5. 查询 ip占用导致ip不通的 问题 查IP对应的mac地址

    IP冲突,  同一个IP配到了多余1台的机器上 ,导致IP 不通的情况,此时需要查询 都有哪台机器配置了这个IP,用 arping 命令, 具体命令 类似于 ping ,直接 arping + 目标地 ...

  6. go context源码解析

    go 的context贯穿整个goroutine的运行控制的中枢,可以实现执行的生命周期的控制. Context是一个接口,他派生了context.emptyCtx(TODO),cancelCtx,t ...

  7. 【数据库开发】如何创建MySQL数据库连接池(一个基于libmysql的MySQL数据库连接池示例(C/C++版))

      http://blog.csdn.net/horace20/article/details/8087557 1.  一般架构说明 图 1 架构层次图 一般应用系统数据库访问模块可大致分为两层,一层 ...

  8. JavaScript中的Truthy和Falsy

    JavaScript中存在Truthy值和Falsy值的概念 — 除了boolean值true.false外,所有类型的JavaScript值均可用于逻辑判断,其规则如下: 1.所有的Falsy值,当 ...

  9. php 的 socket简单原理及实现

    什么是socket socket:网络上的两个程序通过一个双向的通信连接实现数据的交换,连接的一端称为一个socket. 因此socket运行是置少有2个端组成,一个为服务端一个为客户端(客户端可以多 ...

  10. storm是如何保证at least once语义的?

    storm中的一些原语: 要说明上面的问题,得先了解storm中的一些原语,比如: tuple和messagetuple:在storm中,消息是通过tuple来抽象表示的,每个tuple知道它从哪里来 ...