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

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

break;

}

if(state < PM_SUSPEND_MAX && *s)

#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并且对名为mian的wakelock解锁,如果此时没有其余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的处理芯片。依据设备的特点,在已有的管理策略基础之上进行优化。