当用户写入mem 或者 standby到 /sys/power/state中的时候, state_store()会被调用, 然后Android会在这里调用 request_suspend_state() 而标准的Linux会在这里进入enter_state()这个函数. 如果请求的是休眠, 那么early_suspend这个workqueue就会被调用,并且进入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-%02d-%02d %02d:%02d:%02d.%09lu 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
在early_suspend()函数中, 首先会检查现在请求的状态还是否是suspend, 来防止suspend的请求会在这个时候取消掉(因为这个时候用户进程还在运行),如 果需要退出, 就简单的退出了. 如果没有, 这个函数就会把early suspend中 注册的一系列的回调都调用一次, 然后同步文件系统, 然后放弃掉 main_wake_lock, 这个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);}Late Resume
当所有的唤醒已经结束以后, 用户进程都已经开始运行了, 唤醒通常会是以下的几种原因:
来电
如果是来电, 那么Modem会通过发送命令给rild来让rild通知WindowManager有来电响应,这样就会远程调用PowerManagerService来写"on" 到 /sys/power/state 来执行late resume的设备, 比如点亮屏幕等.
用户按键用户按键事件会送到WindowManager中, WindowManager会处理这些 按键事件,按键分为几种情况, 如果案件不是唤醒键(能够唤醒系统的按键) 那么WindowManager会主动放弃wakeLock来使系统进入再次休眠, 如果按键是唤醒键,那么WindowManger就会调用PowerManagerService中的接口来执行 Late Resume.
Late Resume 会依次唤醒前面调用了Early Suspend的设备.
static void late_resume(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 == 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); goto abort; } 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) pos->resume(pos); if (debug_mask & DEBUG_SUSPEND) pr_info("late_resume: done/n");abort: mutex_unlock(&early_suspend_lock);}Wake Lock
我们接下来看一看wake lock的机制是怎么运行和起作用的, 主要关注 wakelock.c文件就可以了.
wake lock 有加锁和解锁两种状态, 加锁的方式有两种, 一种是永久的锁住, 这样的锁除非显示的放开, 是不会解锁的, 所以这种锁的使用是非常小心的. 第二种是超时锁, 这种锁会锁定系统唤醒一段时间, 如果这个时间过去了, 这个锁会自动解除.
锁有两种类型:
WAKE_LOCK_SUSPEND 这种锁会防止系统进入睡眠
WAKE_LOCK_IDLE 这种锁不会影响系统的休眠, 作用我不是很清楚.
在wake lock中, 会有3个地方让系统直接开始suspend(), 分别是:
在wake_unlock()中, 如果发现解锁以后没有任何其他的wake lock了, 就开始休眠
在定时器都到时间以后, 定时器的回调函数会查看是否有其他的wake lock, 如果没有, 就在这里让系统进入睡眠.
在wake_lock() 中, 对一个wake lock加锁以后, 会再次检查一下有没有锁, 我想这里的检查是没有必要的, 更好的方法是使加锁的这个操作原子化, 而 不是繁冗的检查. 而且这样的检查也有可能漏掉.
Suspend
当wake_lock 运行 suspend()以后, 在wakelock.c的suspend()函数会被调用,这个函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用enter_state()来进入Linux的休眠流程..
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 (current_event_num == entry_event_num) { wake_lock_timeout(&unknown_wakeup, HZ / 2); }}Android于标准Linux休眠的区别
pm_suspend() 虽然会调用enter_state()来进入标准的Linux休眠流程,但是还 是有一些区别:
当进入冻结进程的时候, android首先会检查有没有wake lock,如果没有, 才会停止这些进程, 因为在开始suspend和冻结进程期间有可能有人申请了 wake lock,如果是这样, 冻结进程会被中断.
在suspend_late()中, 会最后检查一次有没有wake lock, 这有可能是某种快速申请wake lock,并且快速释放这个锁的进程导致的,如果有这种情况, 这里会返回错误, 整个suspend就会全部放弃.如果pm_suspend()成功了,LOG的输出可以通过在kernel cmd里面增加 "no_console_suspend" 来看到suspend和resume过程中的log输出。
2、系统Suspend和resume的函数流程
取一个例子
加入suspend和resume
mxc_board_init-->mxc_init_bl()-->platform_device_register()-->platform_device_add()-->device_add()-->device_pm_add()-->,最终加入到了dpm_list的链表中,在其中的dpm_suspend和dpm_suspend中通过遍历这个链表来进行查看哪个device中包含suspend和resume项。
Linux进入休眠呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠. 比如
当状态位PM_SUSPEND_ON的状态的时候,调用request_suspend_state();当满足休眠的状态的时候,调用queue_work(suspend_work_queue,&early_suspend_work),调用了early_suspend,然后在其中通过wake_unlock()启动了expire_timer定时器,当定时时间到了,则执行expire_wake_locks,将suspend_work加入到队列中,分析到这里就可以知道了early_suspend_work和suspend_work这两个队列的先后顺序了,suspend调用了pm_suspend,通过判断当前的状态,选择enter_state(),在enter_state中,经过了suspend_prepare,suspend_test和suspend_device_and_enter(),在suspend_device_and_enter中调用了device_suspend来保存状态和结束系统的设备,到了dpm_suspend中结束所有的device。
------------------------------------------------------------------------------
Android的Linux内核的电源管理:Early Suspend
1. 用户空间的接口
在kernel/power/main.c中,定义了一组sysfs的属性文件,其中一个定义是:
power_attr(state);
把这个宏展开后:
<SPAN style=
"COLOR: #0000ff"
>
static
struct
kobj_attribute state_attr = { \
.attr ={ \
.name =
"state"
, \
.mode = 0644, \
}, \
.show =state_show, \
.store =state_store, \
}
</SPAN>
|
我们再看看main.c的入口:
<SPAN style=
"COLOR: #0000ff"
>
static
int
__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);
}
</SPAN>
|
显然,该函数执行后,会在生成/sys/power目录,该目录下会建立一系列属性文件,其中一个是/sys/power/state文件。用户空间向该文件的写入将会导致state_store被调用,读取该文件将会导致state_show函数被调用。
现在回到Android的HAL层中,查看一下代码:hardware/libhardware_legacy/power/power.c:
<SPAN style=
"COLOR: #0000ff"
>
//定义写入/sys/power/state的命令字符串
static
const
char
*off_state =
"mem"
;
static
const
char
*on_state =
"on"
;
//打开/sys/power/state等属性文件,保存相应的文件描述符
static
int
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] =
'\0'
;
len = write(g_fds[REQUEST_STATE], buf,len);
return
0;
}
</SPAN>
|
2. 内核中数据结构和接口
与earlysuspend相关的数据结构和接口都在earlysuspend.h中进行了定义。
- early_suspend 结构
<SPAN style=
"COLOR: #0000ff"
>
struct
early_suspend {
#ifdef CONFIG_HAS_EARLYSUSPEND
structlist_head link;
int
level;
void
(*suspend)(
struct
early_suspend *h);
void
(*resume)(
struct
early_suspend *h);
#endif
};
</SPAN>
|
希望执行early suspend的设备,他的设备驱动程序需要向电源管理系统注册,该结构体用于向电源管理系统注册earlysuspend/lateresume,当电源管理系统启动suspend流程时,回调函数suspend会被调用,相反,resume的最后阶段,回调函数resume会被调用,level字段用于调整该结构体在注册链表中的位置,suspend时,level的数值越小,回调函数的被调用的时间越早,resume时则反过来。Android预先定义了3个level等级:
<SPAN style=
"COLOR: #0000ff"
>
enum
{
EARLY_SUSPEND_LEVEL_BLANK_SCREEN = 50,
EARLY_SUSPEND_LEVEL_STOP_DRAWING = 100,
EARLY_SUSPEND_LEVEL_DISABLE_FB = 150,
};
</SPAN>
|
如果你想你的设备在FB设备被禁止之前执行他的early suspend回调,设备驱动程序应该把level值设定为小于150的某个数值,然后向系统注册early_suspend结构。注册和反注册函数是:
<SPAN style=
"COLOR: #0000ff"
>
void
register_early_suspend(
struct
early_suspend *handler);
void
unregister_early_suspend(
struct
early_suspend *handler);
</SPAN>
|
early_suspend_handlers链表
所有注册到系统中的early_suspend结构都会按level值按顺序加入到全局链表early_suspend_handlers中。
3. 工作流程
首先,我们从kernel/power/wakelock.c中的初始化函数开始:
<SPAN style=
"COLOR: #0000ff"
>
static
int
__init wakelocks_init(
void
)
{
int
ret;
int
i;
......
for
(i = 0; i < ARRAY_SIZE(active_wake_locks); i++)
INIT_LIST_HEAD(&active_wake_locks[i]);
......
wake_lock_init(&main_wake_lock, WAKE_LOCK_SUSPEND,
"main"
);
wake_lock(&main_wake_lock);
wake_lock_init(&unknown_wakeup, WAKE_LOCK_SUSPEND,
"unknown_wakeups"
);
......
ret = platform_device_register(&power_device);
ret = platform_driver_register(&power_driver);
......
suspend_work_queue = create_singlethread_workqueue(
"suspend"
);
......
return
0;
}
</SPAN>
|
可以看到,显示初始化active_wake_locks链表数组,然后初始化并且锁住main_wake_lock,注册平台设备power_device,这些数组、锁和power_device我们在后续文章再讨论,这里我们关注的最后一个动作:创建了一个工作队列线程suspend_work_queue,该工作队列是earlysuspend的核心所在。
系统启动完成后,相关的驱动程序通过register_early_suspend()函数注册了early suspend特性,等待一段时间后,如果没有用户活动(例如按键、触控等操作),用户空间的电源管理服务最终会调用第一节提到的set_screen_state()函数,透过sysfs,进而会调用到内核中的state_store():
<SPAN style=
"COLOR: #0000ff"
>
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;
/* First, check if we are requested to hibernate */
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;
}
</SPAN>
|
紧接着,通过pm_states数组,根据命令字符串查询得到请求的状态,默认情况下,Android的内核都会配置了CONFIG_EARLYSUSPEND,所以会调用request_suspend_state()函数,不过在调用该函数之前会先valid_state()一下,这给了平台相关的代码一个机会确认该平台是否支持所请求的电源状态。valid_state()的具体实现请参考内核代码树。
<SPAN style=
"COLOR: #0000ff"
>
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
(!old_sleep && new_state != PM_SUSPEND_ON) {
state |= SUSPEND_REQUESTED;
if
(queue_work(suspend_work_queue, &early_suspend_work))
pr_info(
"early_suspend_work is in queue already\n"
);
}
else
if
(old_sleep && new_state == PM_SUSPEND_ON) {
state &= ~SUSPEND_REQUESTED;
wake_lock(&main_wake_lock);
if
(!queue_work(suspend_work_queue,&late_resume_work))
pr_info(
"late_resume_work is in queue already\n"
);
}
requested_suspend_state = new_state;
spin_unlock_irqrestore(&state_lock, irqflags);
}
</SPAN>
|
还记得前面初始化时建立的工作队列suspend_woek_queue吗?根据之前的电源状态和请求的状态, request_suspend_state()只是简单地向suspend_work_queue中加入early_suspend_work或者是late_resume_work并调度他们执行。early_suspend_work的工作函数是early_suspend():
<SPAN style=
"COLOR: #0000ff"
>staticvoid 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
) {
}
list_for_each_entry(pos,&early_suspend_handlers, link) {
if
(pos->suspend != NULL) {
if
(debug_mask &DEBUG_SUSPEND)
printk(KERN_DEBUG
"pos->suspend: %pF begin\n"
, pos->suspend);
pos->suspend(pos);
if
(debug_mask &DEBUG_SUSPEND)
printk(KERN_DEBUG
"pos->suspend: %pF finish\n"
, pos->suspend);
}
}
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);
}
</SPAN>
|
终于看到啦,early_suspend()遍历early_suspend_handlers链表,从中取出各个驱动程序注册的early_suspend结构,然后调用它的suspend回调函数。最后,释放main_wake_lock锁,至此整个earlysuspend的流程完成。下面的序列图清晰地表明了整个调用的过程: