1. 电源管理的状态
Android的Linux内核为系统提供了4种电源状态,内核的源码为当中的3种定义了名字和相应的宏定义,名字定义在kernel/power/suspend.c中:
1 2 3 4 5 6 7 |
constchar*const pm_states[PM_SUSPEND_MAX]={ #ifdef CONFIG_EARLYSUSPEND [PM_SUSPEND_ON] ="on", #endif [PM_SUSPEND_STANDBY] ="standby", [PM_SUSPEND_MEM] ="mem", }; |
相应的宏定义在:include/linux/suspend.h中:
1 2 3 4 5 6 |
typedefint __bitwise suspend_state_t;
#define PM_SUSPEND_ON ((__force suspend_state_t) 0) #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 1) #define PM_SUSPEND_MEM ((__force suspend_state_t) 3) #define PM_SUSPEND_MAX ((__force suspend_state_t) 4) |
非常奇怪的是,第四种状态(disk)没有详细的定义,而是硬编码在代码中,不明确为什么会这样做,至少我如今看的版本号是这样(2.6.35),这样的就是所谓的suspend to disk或者叫hibernate。只是这不是重点,再说,眼下也非常少有Android的设备支持hibernate。
顾名思义:
PM_SUSPEND_ON— 设备处于全电源状态,也就是正常工作状态;
PM_SUSPEND_STANDBY— 设备处于省电状态,但还能够接收某些事件,详细的行为取决与详细的设备;
PM_SUSPEND_MEM— suspend to memory,设备进入睡眠状态,但全部的数据还保存在内存中,仅仅有某些外部中断才干够唤醒设备;
眼下,大多数的Android设备都仅仅支持当中的两种:PM_SUSPEND_ON 和 PM_SUSPEND_MEM,所以以下的讨论说道suspend的地方,均是指PM_SUSPEND_MEM。
2. Early Suspend、Late Resume
Early Suspend和Late Resume是Android在标准Linux的基础上添加的一项特性。当用户空间的向内核请求进入suspend时,这时候会先进入early suspend状态,驱动程序能够注冊early suspend的回调函数,当进入该状态时,内核会逐一地调用这些回调函数。比如显示屏的驱动程序一般会注冊early suspend,在他的回调函数中,驱动程序会把屏幕和背光都关闭。在这样的状态下,全部的后台进程都还在活动中,该播放歌曲的播放歌曲,该下载数据的依旧在下载,仅仅是显示屏不良而已。进入early suspend状态以后,一旦全部的电源锁(wake lock)被释放,系统立即会进入真正的suspend流程,直到最后系统停止工作,等待外部事件的唤醒。
3. Android的电源锁机制:wake lock
Android相比标准的Linux内核,在电源管理中增加了wake lock机制。一旦申请了某种类型的锁,电源管理模块将会“锁住”某一种电源状态,眼下,Android提供了两种类型的锁:
WAKE_LOCK_SUSPEND— 阻止系统进入suspend状态;
WAKE_LOCK_IDLE— 阻止系统进入idle状态;
wake lock也能够设定超时,时间一到,自己主动释放该锁。
有关wake lock的代码在:kernel/power/wakelock.c中。
4. 电源状态迁移
内核启动完毕以后,电源管理系统会在sysfs文件系统中建立3个文件:
1 2 3 |
/sys/power/state /sys/power/wake_lock /sys/power/wake_unlock |
电源状态的迁移首先由用户空间的应用程序发起,当系统应用检測到一定时间内没实用户活动后(比如触摸屏、按键),能够向/sys/power/state文件写入对应的电源状态名称(请參考第一节内容),假设写入“mem”,将会触发内核启动suspend的流程,内核将会依照图2.1进行状态的迁移。应用程序也能够通过/sys/power/wake_lock申请一个WAKE_LOCK_SUSPEND 类型的锁,对应地,通过/sys/power/wake_unlock则能够释放一个锁。内核在进入suspend之前假设检測到某个锁没有释放,则会放弃本次的suspend过程,直到这个锁释放为止。
基于Android的Linux内核的电源管理:Early Suspend
1. 用户空间的接口
在kernel/power/main.c中,定义了一组sysfs的属性文件,当中一个定义是:
power_attr(state);
把这个宏展开后:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
staticstruct kobj_attribute state_attr={
.attr={
.name="state",
.mode=0644,
},
.show =state_show,
.store =state_store,
} |
我们再看看main.c的入口:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
staticint __init pm_init(void)
{
......
power_kobj=kobject_create_and_add("power", NULL);
if(!power_kobj)
return-ENOMEM;
return sysfs_create_group(power_kobj,&attr_group);
} |
显然,该函数运行后,会在生成/sys/power文件夹,该文件夹下会建立一系列属性文件,当中一个就是/sys/power/state文件。用户空间向该文件的写入将会导致state_store被调用,读取该文件将会导致state_show函数被调用。
如今回到Android的HAL层中,查看一下代码:hardware/libhardware_legacy/power/power.c:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
//定义写入/sys/power/state的命令字符串
staticconst char*off_state="mem";
staticconst char*on_state="on";
//打开/sys/power/state等属性文件,保存对应的文件描写叙述符
staticint
open_file_descriptors(constchar*const paths[])
{
int i;
for(i=0; i
终于,用户空间的电源管理系统会调用set_screen_state函数来触发suspend的流程,该函数实际上就是往/sys/power/state文件写入"mem"或"on"命令字符串。
<pre lang="c" line="1">int
set_screen_state(inton)
{
......
initialize_fds();
......
char buf[32];
int len;
if(on)
len = snprintf(buf,sizeof(buf),"%s", on_state);
else
len = snprintf(buf,sizeof(buf),"%s", off_state);
buf[sizeof(buf)-1]=' |