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

android电源管理简单分析

PM特点Wakelock机制WakeLock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠(这里的休眠,指的是标准的Linux的休眠ÿ

PM特点


Wakelock机制

WakeLock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠(这里的休眠,指的是标准的Linux的休眠,不包含使用early_suspend()进行休眠的设备,使用early_suspend()的设备,在系统还有wake_lock锁的时候,也是要休眠的),可以被用户态程序和内核获得.这个锁可以是有超时的或者是没有超时的,超时的锁会在时间过去以后自动解锁. 

如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠.

PowerManager.WakeLoc有加锁和解锁两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非显式的放开,是不会解锁的,所以这种锁用起来要非常的小心。第二种锁是超时锁,这种锁会在锁住后一段时间解锁。

在创建了 PowerManager.WakeLock 后,有两种机制,第一种是不计数锁机制,另一种是计数锁机制。可以通过 setReferenceCounted(boolean value) 来指定,一般默认为计数机制。这两种机制的区别在于,前者无论 acquire() 了多少次,只要通过一次 release()即可解锁。而后者正真解锁是在( --count== 0 )的时候,同样当 (count==0) 的时候才会去申请加锁,其他情况 isHeld 状态是不会改变的。所以 PowerManager.WakeLock 的计数机制并不是正真意义上的对每次请求进行申请/释放每一把锁,它只是对同一把锁被申请/释放的次数进行了统计再正真意义上的去操作。一下进行了永久锁的测试: 从测试我们可以看到使用计数和计数锁的区别。


().内核维护了:
1).
两个链表,active_wake_locks[WAKE_LOCK_TYPE_COUNT]
active_wake_locks[0]
维护的是suspendlock.
active_wake_locks[1]
维护的是idlelock.
2).
一个链表,inactive_locks来记录所有处于inactive状态的锁.


(
).下面讲述应用层申请的锁怎么传到kernel下面的,来理解整个wakelock的框架。
比如/sys/power/wake_lock下面的PowerManagerService
的生成过程。
1).Android
提供了现成android.os.PowerManager,类中
提供newWakeLock(intflags, String tag)方法来取得相应
层次的锁,此函数的定义
frameworks/base/core/java/android/os/PowerManager.java
下面,应用程序在申请wake_lock时都会有调用。
实例:
PowerManagerpm =(PowerManager)getSystemService(Context.POWER_SERVICE);
PowerManager.WakeLockwl = pm.newWakeLock
(PowerManager.SCREEN_DIM_WAKE_LOCK, “MyTag”);
wl.acquire();//
申请锁这个里面会调用PowerManagerService里面acquireWakeLock()
wl.release();//
释放锁,显示的释放,如果申请的锁不在此释放系统就不会进入休眠。
2).frameworks

/frameworks/base/services/java/com/android/server/
PowerManagerService.java
这个类是来管理所有的应用程序申请的wakelock。比如音视频播放器,camera等申请的wakelock都是通过这个类来管理的。
staticfinal String PARTIAL_NAME ="PowerManagerService"
Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,
PARTIAL_NAME);
这个函数调用Power类里面的acquireWakeLock(),此时的PARTIAL_NAME作为参数传递到底层去。

/frameworks/base/core/java/android/os/Power.java
publicstatic native void acquireWakeLock(int lock, Stringid);
注:native申明的方法在Power类中没有实现,其实现体在frameworks/base/core/jni/android_os_Power.cpp中,所以调用Power类的acquireWakeLock()方法时会调用JNI下的实现方法。
3).JNI
层的实现
路径:frameworks/base/core/jni/android_os_Power.cpp
staticvoid acquireWakeLock(JNIEnv *env, jobject clazz,
jint lock,jstring idObj)
{
const char *id = env->GetStringUTFChars(idObj,NULL);
acquire_wake_lock(lock,id);
env->ReleaseStringUTFChars(idObj,id);
}
注:在acquireWakeLock()中调用了路径下hardware/libhardware_legacy/power/power.c下面的acquire_wake_lock(lock,id)

4).
kernel层的交互
power.c下的acquire_wake_lock(lock,id)函数如下:
intacquire_wake_lock(int lock, const char* id)
{
 
returnwrite(fd, id, strlen(id));
}
注:fd就是文件描述符,在此表示”/sys/power/wake_lock”id就是从PowerManagerService类中传下来的参数即:
PARTIAL_NAME= "PowerManagerService"


Earlysuspend


Android在标准的Linux休眠与唤醒机制上又加了一层,就是early_suspend/late_resume。顾名思意,使用early_suspend()进行休眠的设备,它休眠的时刻早于其他设备,使用late_resume()唤醒的设备,它被唤醒的时刻要晚于其他设备。这对函数通常成对出现,当内核打开了CONFIG_EARLY_SUSPEND(Android默认打开)后,就可以使用这组函数来代替驱动中标准的suspend/resume接口。在early_suspend()函数中,首先会检查现在请求的状态是否是suspend,以防止suspend的请求在此时撤销(因为此时用户程序还在运行),如果需要退出,就简单的退出了;如果不需要退出,这个函数会把early_suspend中注册的一系列函数都掉用一次,然后同步文件系统,最后放弃main_wake_lock。这个wakelock是没有超时的锁,如果不放弃这个锁那么系统就无法进行休眠。Earlysuspend android引进的一种机制,这种机制在上游备受争议,这里不做评论.这个机制作用在关闭显示的时候,在这个时候,一些和显示有关的设备,比如LCD背光,比如重力感应器,触摸屏,这些设备都会关掉,但是系统可能还是在运行状态(这时候还有wakelock)进行任务的处理,例如在扫描Sd卡上的文件等.在嵌入式设备中,背光是一个很大的电源消耗,所以android会加入这样一种机制.


Staticssize_tstate_store(structkobject*kobj, structkobj_attribute*attr, constchar*buf,size_tn)

{

.........

#ifdefCONFIG_SUSPEND

for (s = &pm_states[state]; state

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

break;

}

if(state

#ifdefCONFIG_EARLYSUSPEND

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

error= 0;

request_suspend_state(state); //这里,进入了Android的休眠与唤醒的处理函数

}

#else

error= enter_state(state);

#endif

#endif

.........

}

power_attr(state);

voidrequest_suspend_state(suspend_state_t new_state)

{

......

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

state|= SUSPEND_REQUESTED;

queue_work(suspend_work_queue,&early_suspend_work);//在休眠的时候,去遍历执行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);//在唤醒的时候,去遍历执行late_resume_work这个队列

}

......

}

//state的值会在0->1->3->2->0循环变化,后面分析代码都可以看出这些值代表系统目前处于什么阶段,简单得说就是:正常->准备进earlysuspend->开始early suspend并且对名为mianwakelock解锁,如果此时没有其余wakelock处于lock状态,那么系统就走linux的休眠唤醒路线让整个系统真正休眠,直到唤醒源发生,然后将处理器和linux层唤醒。之后android层判断本次底层醒来是由于我所定义的唤醒源引起的吗?如果不是,android将不予理会,过段时间没有wakelock锁,系统会再次走linux的休眠路线进入休眠。如果是,那么android上层就会写一个on的指令到state接口中,同样是会调用到函数request_suspend_state()-> 准备执行lateresume -> 开始执行lateresume,之后整个系统就这样被唤醒了

根据用户/系统所请求的状态,去做相应的动作(休眠/唤醒)

能用到的一些变量的声明如下:

staticvoid early_suspend(struct work_struct *work);

staticvoid late_resume(struct work_struct *work);

staticDECLARE_WORK(early_suspend_work, early_suspend);

staticDECLARE_WORK(late_resume_work, late_resume);

early_suspend这个函数指针来处理early_suspend_work这条队列,late_resume这个函数指针来处理late_resume_work这条队列.


staticvoid early_suspend(struct work_struct *work)

{

.......................

if(debug_mask & DEBUG_SUSPEND)

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

list_for_each_entry(pos,&early_suspend_handlers, link) { //这里就是关键了,遍历early_suspend_handler这条链表(在驱动中注册early_suspend的时候,都注册到这条链表上了)

if(pos->suspend != NULL) {

if(debug_mask & DEBUG_VERBOSE)

pr_info("early_suspend:calling %pf\n", pos->suspend);


pos->suspend(pos);//调用各个实现进行各设备的休眠

}

}

.......................

if(state == SUSPEND_REQUESTED_AND_SUSPENDED)


wake_unlock(&main_wake_lock);//wake_lock主要用来防止系统休眠,也就是说,只要系统中其他地方还拥有wake_lock锁(类型WAKE_LOCK_SUSPEND),系统就没法进入休眠,如果没有锁了,那就要接着走标准Linux的那一套休眠机制了

......................

}


由于屏幕的耗电比重很大,所以当屏幕关闭的时候会关掉背光,重力感器等设备,然后等到没有程序持有唤醒的时候进入真正睡眠,但是标准的linux并不一定睡眠。


Lateresume

LateResume是和suspend配套的一种机制,是在内核唤醒完毕开始执行的.主要就是唤醒在EarlySuspend的时候休眠的设备

当所有的唤醒已经结束以后,用户进程都已经开始运行了,唤醒通常会是以下的几种原因:

(1)来电

如果是来电,那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写"on"/sys/power/state来执行lateresume的设备,比如点亮屏幕等.

(2)用户按键

用户按键事件会送到WindowManager,WindowManager会处理这些按键事件,按键分为几种情况,如果案件不是唤醒键(能够唤醒系统的按键)那么WindowManager会主动放弃wakeLock来使系统进入再次休眠,如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行LateResume.LateResume会依次唤醒前面调用了EarlySuspend的设备.

Staticvoid late_resume(struct work_struct *work)

{

staticvoid late_resume(struct work_struct *work)

{

structearly_suspend *pos;

unsignedlong irqflags;

intabort = 0;

mutex_lock(&early_suspend_lock);

spin_lock_irqsave(&state_lock,irqflags);

if(state == SUSPENDED)

state&= ~SUSPENDED;

else

abort= 1;

spin_unlock_irqrestore(&state_lock,irqflags);

if(abort) {

if(debug_mask & DEBUG_SUSPEND)

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

gotoabort;

}

if(debug_mask & DEBUG_SUSPEND)

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

list_for_each_entry_reverse(pos,&early_suspend_handlers, link) {

if(pos->resume != NULL) {

if(debug_mask & DEBUG_VERBOSE)

pr_info("late_resume:calling %pf\n", pos->resume);

//遍历链表依次调用每个驱动注册的resume handler.

pos->resume(pos);

//恢复操作

}

}

if(debug_mask & DEBUG_SUSPEND)

pr_info("late_resume:done\n");

abort:

mutex_unlock(&early_suspend_lock);

}

}

评价


android系统基于linux系统针对移动设备的特点做了改进提高,但是从机制上讲还有所不成熟的地方。


由于程序不合理的占有WakeLock导致系统无法进入休眠。Android电源管理中没有相应的机制对这种情况作出判断。或者说没有完整性检查的步骤。


没有考虑外设的特点,而仅仅有earlysuspend lateresume这种统一的回调接口,而缺乏更细粒度的依据设备特点的管理,比如CPU的不同频率的管理,屏幕亮度管理,Wifi芯片的工作机制管理,这里边很多机制的实现有赖于具体的芯片,有些则需要系统整体考量,进行统一的回调处理。如系统会评估当前的任务,和当前的运行器件,给出一个20%电量的时候高优先设备,低优先级设备应该去实现的回调接口。


没有针对嵌入式设备的特点,对启动过程进行具体设计。而只是在已有的机制上面加入必要性的扫描机制。缺乏如快照启动之类的支持。


缺乏用户个性化的手机使用资料收集及统计,缺乏个性化的电源管理方案支持。如果有这方面的资料则可以定制化电源管理。


缺乏任务调度和电量消耗及用户体验之间的关系研究。


我们的工作


我们的工作就在于依据具体的设备进行电源管理优化,考虑具体设备的特点如Broadcom芯片,Omap的处理芯片。依据设备的特点,在已有的管理策略基础之上进行优化。



推荐阅读
  • 本文讨论了一个关于cuowu类的问题,作者在使用cuowu类时遇到了错误提示和使用AdjustmentListener的问题。文章提供了16个解决方案,并给出了两个可能导致错误的原因。 ... [详细]
  • 如何使用Java获取服务器硬件信息和磁盘负载率
    本文介绍了使用Java编程语言获取服务器硬件信息和磁盘负载率的方法。首先在远程服务器上搭建一个支持服务端语言的HTTP服务,并获取服务器的磁盘信息,并将结果输出。然后在本地使用JS编写一个AJAX脚本,远程请求服务端的程序,得到结果并展示给用户。其中还介绍了如何提取硬盘序列号的方法。 ... [详细]
  • Java在运行已编译完成的类时,是通过java虚拟机来装载和执行的,java虚拟机通过操作系统命令JAVA_HOMEbinjava–option来启 ... [详细]
  • Iamtryingtomakeaclassthatwillreadatextfileofnamesintoanarray,thenreturnthatarra ... [详细]
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了如何使用php限制数据库插入的条数并显示每次插入数据库之间的数据数目,以及避免重复提交的方法。同时还介绍了如何限制某一个数据库用户的并发连接数,以及设置数据库的连接数和连接超时时间的方法。最后提供了一些关于浏览器在线用户数和数据库连接数量比例的参考值。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • Spring特性实现接口多类的动态调用详解
    本文详细介绍了如何使用Spring特性实现接口多类的动态调用。通过对Spring IoC容器的基础类BeanFactory和ApplicationContext的介绍,以及getBeansOfType方法的应用,解决了在实际工作中遇到的接口及多个实现类的问题。同时,文章还提到了SPI使用的不便之处,并介绍了借助ApplicationContext实现需求的方法。阅读本文,你将了解到Spring特性的实现原理和实际应用方式。 ... [详细]
  • 后台获取视图对应的字符串
    1.帮助类后台获取视图对应的字符串publicclassViewHelper{将View输出为字符串(注:不会执行对应的ac ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • FeatureRequestIsyourfeaturerequestrelatedtoaproblem?Please ... [详细]
  • Android源码深入理解JNI技术的概述和应用
    本文介绍了Android源码中的JNI技术,包括概述和应用。JNI是Java Native Interface的缩写,是一种技术,可以实现Java程序调用Native语言写的函数,以及Native程序调用Java层的函数。在Android平台上,JNI充当了连接Java世界和Native世界的桥梁。本文通过分析Android源码中的相关文件和位置,深入探讨了JNI技术在Android开发中的重要性和应用场景。 ... [详细]
  • 本文介绍了如何使用C#制作Java+Mysql+Tomcat环境安装程序,实现一键式安装。通过将JDK、Mysql、Tomcat三者制作成一个安装包,解决了客户在安装软件时的复杂配置和繁琐问题,便于管理软件版本和系统集成。具体步骤包括配置JDK环境变量和安装Mysql服务,其中使用了MySQL Server 5.5社区版和my.ini文件。安装方法为通过命令行将目录转到mysql的bin目录下,执行mysqld --install MySQL5命令。 ... [详细]
  • 本文介绍了iOS数据库Sqlite的SQL语句分类和常见约束关键字。SQL语句分为DDL、DML和DQL三种类型,其中DDL语句用于定义、删除和修改数据表,关键字包括create、drop和alter。常见约束关键字包括if not exists、if exists、primary key、autoincrement、not null和default。此外,还介绍了常见的数据库数据类型,包括integer、text和real。 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
author-avatar
enochsun
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有