您的位置:首页 > 移动开发 > Android开发

android内核的电源管理(PM)

2014-03-25 20:32 260 查看
Android定义了几种低功耗的状态:earlysuspend(挂起早期),suspend(挂起),hibernation(休眠)。之所以增加了节能的复杂性,主要考虑到android设备在有程序运行时同样需要进行节能这种特殊需求,以及考虑了外设(比如wifi、led等)单独节能的需求。

其中earlysuspend是一种低功耗的状态,某些设备可以选择进入某种功耗较低的状态,比如LCD可以降低亮度或灭掉。此状态为android新增状态。

syspend指除电源管理以外的其它外围模块以及cpu均不工作,只有内存保持自刷新的状态。此状态的进入方式android有修改。

Hibernation指所有内存镜像都被写入磁盘中,然后系统关机,恢复后系统能恢复到关机前状态。

电源管理源代码主要在/kernel/power/文件夹下。用户可以通过读写sys文件/sys/power/state实现控制系统进入低功耗状态。用户通过将const char * const pm_states[] 中定义的字符串, 比如“on”对应非节能状态,“mem”,“standby”对应earlysuspend,“disk”对应hibernation,wake lock 机制进入suspend状态。写入/sys/power/state进行功耗控制,会调用到state_store():

static ssize_tstate_store(struct kobject *kobj, struct kobj_attribute *attr,

            const char *buf,size_t n)

{

#ifdef CONFIG_SUSPEND

#ifdefCONFIG_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;

首先判断用户写入的是否是“disk”字符串,如果是则调用hibernate()函数命令系统进入hibernation状态

/* First, check if we are requested to hibernate */

if (len == 4 && !strncmp(buf, "disk", len)) {

     error = hibernate();

  goto Exit;

}

如果是其他字符串则且定义了CONFIG_EARLYSUSPEND调用request_suspend_state(),如果未定义了CONFIG_EARLYSUSPEND调用enter_state()

#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)

#ifdefCONFIG_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;

}

request_suspend_state()函数是android相对标准linux改动的地方。在标准linux内核中,用户通过 sysfs 写入“mem”和“standby”时,会直接调用enter_state()进入suspend模式,但在android中则会调用request_suspend_state()函数进入early suspend状态。request_suspend_state()函数代码如下:

void request_suspend_state(suspend_state_tnew_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) {//如果是PM_SUSPEND_STANDBY或PM_SUSPEND_MEM,排队一个 early_suspend_work

     state |= SUSPEND_REQUESTED;

     queue_work(suspend_work_queue, &early_suspend_work);

} else if (old_sleep && new_state == PM_SUSPEND_ON) {
//如果是PM_SUSPEND_ON,是唤醒请求,排队 late_resume_work

     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_work和late_resume_work两工作队列定义为 :

staticDECLARE_WORK(early_suspend_work, early_suspend);

static DECLARE_WORK(late_resume_work,late_resume);

可见实际工作的是early_suspend和late_resume这两个函数。

函数early_suspend和late_resume完成的事情很简单,就是遍历这个链表,依次调用每个设备注册的handler,late_resume是唤醒处于early_suspend的那些设备。代码如下:

static voidearly_suspend(struct work_struct *work)

{

… …

if (debug_mask &DEBUG_SUSPEND)

pr_info("early_suspend: callhandlers\n");

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

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);

}

 

static voidlate_resume(struct work_struct *work)

{

… …

if (debug_mask &DEBUG_SUSPEND)

pr_info("late_resume: callhandlers\n");

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

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);

}

register_early_suspend函数完成的功能就是把驱动提供的earlysuspend handler挂到early_suspend_handler链表上。unregister_early_suspend则相反,从链表上摘下handler。

例如:fbearlysuspend.c中针对lcd framebuffer的earlysuspend支持

    register_early_suspend(&stop_drawing_early_suspend_desc);

Hibernate.c文件实现hibernation低功耗状态,是最彻底的低功耗模式,它把所有内存镜像都写入磁盘中,然后系统关机。这块代码跟标准Linux内核基本一致。

最后分析suspend状态增加的wakelock机制。实现在wakelock.c和userwakelock.c文件中。wakelock可以阻止处于正常运行(active)或者空闲(idle)状态的系统进入睡眠等低功耗状态。直到所持有的wakelock全部被释放,系统才能进入低功耗的状态。 (是指看视频的时候不希望LCD黑屏吧)

加锁的方式有两种:

一种是永久的锁住,这样的锁除非显示的释放,是不会解锁的。第二种是超时锁,这种锁会锁定系统一段时间,如果这个时间过去了,这个锁会自动解除。

锁的类型有两种:

WAKE_LOCK_SUSPEND:这种锁会防止系统进入suspend。

WAKE_LOCK_IDLE :防止进入idle下的低能耗状态。

Android使用两条双向链表active_wake_locks[WAKE_LOCK_TYPE_COUNT]分别保存处于active状态的suspend lock和 idle lock;使用一条链表inactive_locks记录所有处于inactive状态的锁。

在系统启动的时候,会调用wakelocks_init函数来完成wakelock的初始化,但驱动程序也可以再单独创建需要使用的wakelock,这里初始化的是系统默认的wake lock以及该机制依赖的功能。

加锁有两个函数:wake_lock(struct wake_lock *lock)和wake_lock_timeout(struct wake_lock *lock, longtimeout),前者是没有指定过期时间的(除非显式调用wake_unlock否则永远锁住); 后者是有过期时间的(时间过期后,锁会解锁,即使没有显式调用wake_unlock)。这两个函数内部都是通过调用wake_lock_internal()函数完成具体功能的。

wake_lock_internal()函数流程: 

static voidwake_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));

#ifdefCONFIG_WAKELOCK_STAT

判断锁的类型是否有效,即是否为WAKE_LOCK_SUSPEND,如果配置了CONFIG_WAKELOCK_STAT,则更新struct wake_lock里面的用于统计锁信息的成员变量

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++;

}

 

将锁从inactive_locks链表上取下,加到active_wake_locks链表上。如果是超期锁则设置锁的flag置为WAKE_LOCK_AUTO_EXPIRE,否则取消WAKE_LOCK_AUTO_EXPIRE标志。

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;

#ifdefCONFIG_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]);

}

对于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,否则返回的是所有超期锁过期时间的最大值


if (type == WAKE_LOCK_SUSPEND) {

     current_event_num++;

#ifdefCONFIG_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

如果has_wake_lock_locked函数返回的是-1(表示当前活动锁有非超时锁)或者0(表示所有活动锁都是超时锁,且全已经超时),则删除expire_timer,并排队到suspend_work_queue工作队列,最终系统会suspend 。否则修改超时值重启定时器继续等待全部锁超时。

     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 expiretimer\n",

                     lock->name);

         if (expire_in == 0)

             queue_work(suspend_work_queue, &suspend_work);

     }

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

定时器expire_timer 注册的到时回调函数expire_wake_locks是实现超时锁机制的关键,定时器的expire时间被设置为当前所有处于活动状态的WAKE_LOCK_SUSPEND锁超时值的最大值,如果没有超时锁则设置stop它。当定时器expire的时候,会在其处理函数expire_wake_locks中调用has_wake_lock_locked函数把所有过期的锁全部解锁,并排队一个suspend工作到suspend_work_queue工作队列,最终系统会suspend。

static voidexpire_wake_locks(unsigned long data)

{

long has_lock;

unsigned long irqflags;

if (debug_mask &DEBUG_EXPIRE)

pr_info("expire_wake_locks:start\n");

spin_lock_irqsave(&list_lock,irqflags);

if (debug_mask &DEBUG_SUSPEND)

print_active_locks(WAKE_LOCK_SUSPEND);

has_lock =has_wake_lock_locked(WAKE_LOCK_SUSPEND);

if (debug_mask &DEBUG_EXPIRE)

pr_info("expire_wake_locks: done,has_lock %ld\n", has_lock);

if (has_lock == 0)

queue_work(suspend_work_queue,&suspend_work);

spin_unlock_irqrestore(&list_lock,irqflags);

}

suspend函数完成suspend系统的任务,它是suspend_work这个工作的处理函数,该函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用 enter_state()来进入 linux的suspend流程。

static voidsuspend(struct work_struct *work)

{

int ret;

int entry_event_num;

struct timespec ts_entry, ts_exit;

 

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");

getnstimeofday(&ts_entry);

ret = pm_suspend(requested_suspend_state);

getnstimeofday(&ts_exit);

 

if (debug_mask & DEBUG_EXIT_SUSPEND) {

     struct rtc_time tm;

     rtc_time_to_tm(ts_exit.tv_sec, &tm);

     pr_info("suspend: exit suspend, ret = %d "

         "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n", ret,

         tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,

         tm.tm_hour, tm.tm_min, tm.tm_sec, ts_exit.tv_nsec);

}

 

if (ts_exit.tv_sec - ts_entry.tv_sec <= 1) {

     ++suspend_short_count;

 

     if (suspend_short_count == SUSPEND_BACKOFF_THRESHOLD) {

         suspend_backoff();

         suspend_short_count = 0;

     }

} else {

     suspend_short_count = 0;

}

 

if (current_event_num == entry_event_num) {

     if (debug_mask & DEBUG_SUSPEND)

         pr_info("suspend: pm_suspend returned with noevent\n");

     wake_lock_timeout(&unknown_wakeup, HZ / 2);

}

}

解锁函数由wake_unlock函数实现:

void wake_unlock(structwake_lock *lock)

{

int type;

unsigned long irqflags;

spin_lock_irqsave(&list_lock, irqflags);

type = lock->flags & WAKE_LOCK_TYPE_MASK;

#ifdefCONFIG_WAKELOCK_STAT

wake_unlock_stat_locked(lock, 0);

#endif

if (debug_mask & DEBUG_WAKE_LOCK)

     pr_info("wake_unlock: %s\n", lock->name);

将该锁从active链表转移到inactive链表中

lock->flags &= ~(WAKE_LOCK_ACTIVE | WAKE_LOCK_AUTO_EXPIRE);

list_del(&lock->link);

list_add(&lock->link, &inactive_locks);

if (type == WAKE_LOCK_SUSPEND) {

如果是WAKE_LOCK_SUSPEND锁,查看所有处于active状态并且具有自动过期属性的锁(超时锁),遍历找到最晚过期时间

     long has_lock = has_wake_lock_locked(type);

     if (has_lock > 0) {

         if (debug_mask & DEBUG_EXPIRE)

             pr_info("wake_unlock: %s, start expire timer,"

                 "%ld\n", lock->name, has_lock);

修改expire_timer的到期时间

         mod_timer(&expire_timer, jiffies + has_lock);

     } else {

如果has_wake_lock_locked为-1表示存在一个不具有auto-expire属性的锁(非超期锁),则会导致expire_timer被stop(只要存在非超期锁就没有必要定时了)

         if (del_timer(&expire_timer))

             if (debug_mask & DEBUG_EXPIRE)

                 pr_info("wake_unlock: %s, stop expire "

                     "timer\n", lock->name);

如果为0表示所有超时锁到期,且没有非超期锁,此时可以挂起。

         if (has_lock == 0)

             queue_work(suspend_work_queue, &suspend_work);

     }

     if (lock == &main_wake_lock) {

         if (debug_mask & DEBUG_SUSPEND)

             print_active_locks(WAKE_LOCK_SUSPEND);

#ifdefCONFIG_WAKELOCK_STAT

         update_sleep_wait_stats_locked(0);

#endif

     }

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

userwakelock.c文件实现的是wakelock机制的sysfs接口,用户可以通过这个接口操作锁,加锁或解锁。该文件是标准的sysfs接口函数,提供了wake_lock_show、wake_lock_store、wake_unlock_show 和wake_unlock_store四个函数,这样用户可以通过echo,cat等命令写入或读出系统中wake lock。见用户接口 。

用户接口

电源管理内核层给应用层提供的接口就是sysfs 文件系统,所有的相关接口都通过sysfs实现。Android上层frameworks也是基于sysfs做了包装,最终提供给Android java应用程序的是java类的形式。

Android系统会在sysfs里面创建以entry:

/sys/power/state

/sys/power/wake_lock

/sys/power/wake_unlock

 

echo mem >/sys/power/state 或

echo standby >/sys/power/state

以上2个命令系统进入earlysuspend状态,那些注册了early suspend handler的驱动将依次进入各自的earlysuspend 状态。 on退出early suspend状态

echo on >/sys/power/state

系统进入hibernation状态:

echo disk >/sys/power/state

加锁“lockname”:

echo lockname >/sys/power/wake_lock

解锁“lockname”:

echo lockname >/sys/power/wake_unlock

上述是分别加锁和解锁的命令,一旦系统中所有wakelock被解锁,系统就会进入suspend状态,可见Android中原本使系统suspend 的操作(echo mem > /sys/power/state 等)被替换成使系统进入early suspend;而wake lock 机制成为用户命令系统进入suspend状态的唯一途径。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息