一、简介

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层)。

下图是Android睡眠唤醒模块框架:

 二、相关代码

• 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 的用户接口进行操作,从而触发内核态实现的功能。

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

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

PowerManager.acquire()->
PowerManager.acquireLocked()->
PowerManagerService.acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws)->
PowerManagerService.acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid,
String tag, WorkSource ws)->
Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME)->
android_os_Power.cpp::acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj)->
power.c::acquire_wake_lock(int lock, const char* id)->
write(fd, id, strlen(id))

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

ssize_t wake_lock_store(
struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
long timeout;
struct user_wake_lock *l;
mutex_lock(&tree_lock);
l = lookup_wake_lock_name(buf, 1, &timeout);
if (IS_ERR(l)) {
n = PTR_ERR(l);
goto bad_name;
}
if (debug_mask & DEBUG_ACCESS)
pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);
if (timeout)
wake_lock_timeout(&l->wake_lock, timeout);
else
wake_lock(&l->wake_lock);
bad_name:
mutex_unlock(&tree_lock);
return n;
}

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

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

static void wake_lock_internal(
struct wake_lock *lock, long timeout, int has_timeout)
{
int type;
unsigned long irqflags;
long expire_in;
spin_lock_irqsave(&list_lock, irqflags);
type = lock->flags & WAKE_LOCK_TYPE_MASK;
BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);
BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));
#ifdef CONFIG_WAKELOCK_STAT
if (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {
if (debug_mask & DEBUG_WAKEUP)
pr_info("wakeup wake lock: %s\n", lock->name);
wait_for_wakeup = 0;
lock->stat.wakeup_count++;
}
if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&
(long)(lock->expires - jiffies) <= 0) {
wake_unlock_stat_locked(lock, 0);
lock->stat.last_time = ktime_get();
}
#endif
if (!(lock->flags & WAKE_LOCK_ACTIVE)) {
lock->flags |= WAKE_LOCK_ACTIVE;
#ifdef CONFIG_WAKELOCK_STAT
lock->stat.last_time = ktime_get();
#endif
}
list_del(&lock->link);
if (has_timeout) {
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",
lock->name, type, timeout / HZ,
(timeout % HZ) * MSEC_PER_SEC / HZ);
lock->expires = jiffies + timeout;
lock->flags |= WAKE_LOCK_AUTO_EXPIRE;
list_add_tail(&lock->link, &active_wake_locks[type]);
} else {
if (debug_mask & DEBUG_WAKE_LOCK)
pr_info("wake_lock: %s, type %d\n", lock->name, type);
lock->expires = LONG_MAX;
lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;
list_add(&lock->link, &active_wake_locks[type]);
}
if (type == WAKE_LOCK_SUSPEND) {
current_event_num++;
#ifdef CONFIG_WAKELOCK_STAT
if (lock == &main_wake_lock)
update_sleep_wait_stats_locked(1);
else if (!wake_lock_active(&main_wake_lock))
update_sleep_wait_stats_locked(0);
#endif
if (has_timeout)
expire_in = has_wake_lock_locked(type);
else
expire_in = -1;
if (expire_in > 0) {
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, start expire timer, "
"%ld\n", lock->name, expire_in);
mod_timer(&expire_timer, jiffies + expire_in);
} else {
if (del_timer(&expire_timer))
if (debug_mask & DEBUG_EXPIRE)
pr_info("wake_lock: %s, stop expire timer\n",
lock->name);
if (expire_in == 0)
queue_work(suspend_work_queue, &suspend_work);
}
}
spin_unlock_irqrestore(&list_lock, irqflags);
}

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流程。
四、系统进入睡眠(suspend)
       当按了MID上的power键,经过一系统列的事务处理后,它会调用到PowerManager类中的goToSleep。在用户态的调用流程如下所示:

PowerManager.goToSleep(long time)->
PowerManagerService.goToSleep(long time)->
PowerManagerService.goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER)->
PowerManagerService.goToSleepLocked(long time, int reason)->
PowerManagerService.setPowerState(SCREEN_OFF, false, reason)->
PowerManagerService.sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER)
PowerManagerService.setScreenStateLocked(true)->
Power.setScreenState->
android_os_Power.cpp::setScreenState->
Power.c::set_screen_state(int on)->
write(g_fds[REQUEST_STATE], buf, len)

上面的write将把"mem"写入/sys/power/state中。接下来,在kernel态,state_store函数将被调用。

• Android特有的earlysuspend: request_suspend_state(state)
      • Linux标准的suspend:       enter_state(state)

在state_store中,若定义了CONFIG_EARLYSUSPEND,则执行request_suspend_state(state)以先进入earlysuspend,然后根据wake_lock的状态决定是否进入suspend;否则直接执行enter_state(state)以进入suspend状态。

Android睡眠唤醒机制--系统架构相关推荐

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

    Android睡眠唤醒机制--Kernel态 转载 2014年08月26日 14:31:41 标签: android / hibernate 193 一.简介 Android系统中定义了几种低功耗状态 ...

  2. Android/linux(earlysuspend、lateresume)睡眠唤醒机制简

    来源处 http://blog.sina.com.cn/s/blog_759dc36b0100stax.html 背景介绍: 睡眠/唤醒是嵌入式Linux非常重要的组成部分,因为优秀的睡眠唤醒机制可以 ...

  3. Linux睡眠唤醒机制分析--以IMX6UL为例

    鉴于当前做的项目中有低功耗的需求,因此查探了一番Linux的睡眠及唤醒的机制. 当前网络上已经有很多关于睡眠唤醒的分析文章,有的分析也非常透彻,因此本文只从寄存器以及汇编处理和CPU架构方面来补充一下 ...

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

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

  5. Linux睡眠唤醒机制--Kernel态

    首先给大家分享一个巨牛巨牛的人工智能教程,是我无意中发现的.教程不仅零基础,通俗易懂,而且非常风趣幽默,还时不时有内涵段子,像看小说一样,哈哈-我正在学习中,觉得太牛了,所以分享给大家!点这里可以跳转 ...

  6. 【android睡眠唤醒 二】MTK平台唤醒框架分解

    在文章MTK 唤醒时间分析中分析了内核中的主要的亮屏重要阶段,此篇文章结合上层的log一起来分析下整个系统的亮屏流程.整个流程可以分为如下几个部分: (1)power键(home键)产生并上报(在in ...

  7. linux待机唤醒_Linux睡眠唤醒机制--Kernel态

    一.对于休眠(suspend)的简单介绍 在Linux中,休眠主要分三个主要的步骤: 1) 冻结用户态进程和内核态任务 2) 调用注册的设备的suspend的回调函数, 顺序是按照注册顺序 3) 休眠 ...

  8. android app唤醒机制,Android平台下APP唤醒机制

    方案一可以使用以下1.2两种方法实现,方案二可以使用以下方法3实现.(部分转载) 1.使用隐匿intent的方式调用,比较简单 首先在自己应用的Mainifest.xml中加入如下代码: androi ...

  9. 【Android 源码学习】系统架构和启动流程

    Android 源码学习 系统架构和启动流程 望舒课堂 学习记录整理.以及以下参考文章的整理汇总.便于我个人的学习记录. 感谢IngresGe,Gityuan的精彩文章.为我们这些初探android系 ...

最新文章

  1. java字符生成器_Java实现简单字符生成器代码例子
  2. 深度学习框架TensorFlow(3.变量)
  3. 从操作系统的PV操作理解JAVA的synchronized同步方法,同步代码块实现,及比较
  4. RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)
  5. PHP代码优化的细节
  6. html画布图片不显示_如何在HTML5画布上显示图像
  7. SQL-ALTER-change和modify区别
  8. 在Project中引用zedgraph控件
  9. monkey测试小结
  10. WifiConnectivityManager 管理扫描
  11. Julia:last() 和first()
  12. java实现调查问卷_jsp70516调查问卷自动生成与分析系统 双数据库 mysql版
  13. 【CDN学习笔记6】CDN回源到阿里云主机被拒绝的案例
  14. 无敌破坏王-高清在线观看
  15. LightGBM算法详解(教你一文掌握LightGBM所有知识点)
  16. [ vulhub漏洞复现篇 ] Apereo-cas 4.1 反序列化远程代码执行漏洞
  17. 基于 VPX 总线的工件台运动控制系统研究与开发-DSP+FPGA硬件架构(一)
  18. Google hosts文件
  19. 马云个人名义捐款华为_中国富豪慈善捐款排行:第一名【马云】
  20. MathType中输入矩阵转置符号(向下丁字符号 Down tack,形状类似大写字母T)

热门文章

  1. Red Hat Enterprise Linux (RHEL) 9 更新了什么,即 Rocky Linux 9 和 AlmaLinux 9 展望
  2. 年度总结思维导图模板(附软件注册码序列号)
  3. python中true是什么意思_python中的true是什么
  4. 空间计量经济学与Stata操作
  5. 基于matlab的循环卷积,用MATLAB实现循环卷积.doc
  6. 代码迁移_三种类型的代码迁移
  7. 可去间断点的导数存在吗?
  8. 解决snmp不能使用外部地址访问问题
  9. java 根据IP地址获取地理位置
  10. Jenkins 凭证管理