Android内核驱动——电源管理
13.1 基本原理
Android 中定义了几种低功耗状态:earlysuspend,suspend,hibernation。
 earlysuspend是一种低功耗的状态,某些设备可以选择进入某种功耗较低的状态,比如 LCD可以降低亮度或灭掉;
 suspend是指除电源管理以外的其他外围模块以及cpu均不工作,只有内存保持自刷新的状态;
 hibernation是指所有内存镜像都被写入磁盘中,然后系统关机,恢复后系统将能恢复到“关机”之前的状态。
13.2 电源管理机制的实现
电源管理机制的源代码主要在kernel/power/文件夹下面。
main.c文件是整个框架的入口。用户可以通过读写sys文件/sys/power/state实现控制系统进入低功耗状态。用户对于/sys/power/state的读写会调用到main.c中的state_store(),用户可以写入const char * const pm_states[] 中定义的字符串, 比如“on”,“mem”,“standby”,“disk”。
state_store()首先判断用户写入的是否是“disk”字符串,如果是则调用hibernate()函数命令系统进入hibernation状态。如果是其他字符串则调用request_suspend_state()(如果未定义 CONFIG_EARLYSUSPEND)或者调用enter_state()(如果未定义CONFIG_EARLYSUSPEND)。
request_suspend_state()函数是android相对标准linux改动的地方,它实现在earlysuspend.c中。在标准linux内核中,用户通过 sysfs 写入“mem”和“standby”时,会直接调用enter_state()进入suspend模式,但在android中则会调用request_suspend_state()函数进入early suspend状态。request_suspend_state()函数代码如下: 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; //判断是否为省电请求,如果是排队一个 early_suspend_work 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); //否则,是唤醒请求,排队 late_resume_work 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定义为 static DECLARE_WORK(early_suspend_work, early_suspend); static DECLARE_WORK(late_resume_work, late_resume);
可见实际工作的是early_suspend和late_resume这两个函 数。Android提供了register_early_suspend和unregister_early_suspend两个函数供驱动调用,分别完成设备earlysuspend的注册和注销。系统将所有注册支持early_suspend的设备驱动对应的 handler挂在一个称为early_suspend_handler的链表上。函数early_suspend和late_resume完成的事情很简单,就是遍历这个链表,依次调用每个设备注册的handler,late_resume是唤醒处于early_suspend的那些设备。代码如下: 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"); //遍历链表依次调用每个驱动的 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 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"); //遍历链表依次调用每个驱动注册的 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和consoleearlysuspend.c这两个文件实现了针对lcd framebuffer的earlysuspend支持和console的earlysuspend支持。实际上这两个文件就是利用上面earlysuspend.c提供的接口注册了针对framebuffer和console的early suspend handler,并提供相应的handler函数。
Hibernate.c文件实现hibernation低功耗状态,是最彻底的低功耗模式,它把所有内存镜像都写入磁盘中,然后系统关机。该文件还在sysfs文件系统中创建了多个entry,分别是/sys/power/disk,/sys/power/resume和/sys/power/image_size,这样用户可以直接通过 sysfs 来控制系统进出hibernation状态。这块代码跟标准Linux内核没有什么区别。
Android改动较大的另一处是增加了wakelock机制。实现在wakelock.c和userwakelock.c中。wakelock可以阻止处于正常运行(active)或者空闲(idle)状态的系统进入睡眠等低功耗状态。直到所持有的wakelock全部被释放,系统才能进入睡眠等低功耗的状态。
wakelock有加锁和解锁两种状态,加锁的方式有两种,一种是永久的锁住,这样的锁除非显示的放开,是不会解锁的。第二种是超时锁,这种锁会锁定系统一段时间,如果这个时间过去了,这个锁会自动解除。
锁有两种类型:
 WAKE_LOCK_SUSPEND:这种锁会防止系统进入睡眠,这种锁可以具有WAKE_LOCK_AUTO_EXPIRE属性,具有这种属性的锁称为超时锁(timeout)。
 WAKE_LOCK_IDLE 这种锁不会影响系统的suspend,用于阻止系统持有锁的过程中进入low power的状态。
Android使用两条双向链表active_wake_locks[WAKE_LOCK_TYPE_COUNT]分别保存处于active状态的suspend lock和 idle lock; 使用一条链表inactive_locks记录所有处于inactive状态的
锁。
在系统启动的时候,会调用wakelocks_init函数来完成wakelock的初始化,但别的驱动程序也可以再单独创建自用的wakelock,这里初始化的是系统默认的wake lock以及该机制依赖的功能。
wakelocks_init函数做了以下事情:
 初始化acitive_wake_locks链表
 调用wake_lock_init初始化main_wake_lock, unknown_wakeup以及deleted_wake_locks(如果CONFIG_WAKELOCK_STAT被定义)三个WAKE_LOCK_SUSPEND型的锁
 调用platform_device_register和platform_driver_register注册平台设备和驱动。
 创建suspend_work_queue工作队列,这会在wake_unlock解锁的时候用到。wake_lock_init()函数初始化一个锁,就是初始化表示一个wakelock的数据结构struct wake_lock,并将其挂到inactive_locks链表上。
 加锁有两个函数:wake_lock(struct wake_lock *lock)和ake_lock_timeout(struct wake_lock *lock, long timeout),前者是没有指定过期时间的(除非显式调用wake_unlock否则永远锁住); 后者是有过期时间的(时间过期后,锁会解锁,即使没有显式调用wake_unlock)。这两个函数内部都是通过调用wake_lock_internal()函数完成具体功能的。
wake_lock_internal()函数流程:
 判断锁的类型是否有效,即是否为WAKE_LOCK_SUSPEND或WAKE_LOCK_IDLE某一种
 如果定义了CONFIG_WAKELOCK_STAT, 则更新struct wake_lock里面的用于统计锁信息的成员变量
 将锁从inactive_locks链表上取下,加到active_wake_locks链表上。如果是超期锁则设置锁的flag|=WAKE_LOCK_AUTO_EXPIRE,否则取消WAKE_LOCK_AUTO_EXPIRE标志。如果锁是WAKE_LOCK_SUSPEND型的,则继续下面的步骤。
 对于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,否则它最终返回的是所有超期锁过期时间的最大值
 如果has_wake_lock_locked函数返回的是-1(表示当前活动锁有非超时锁)或者0(表示所有活动锁都是超时锁,且全已经超时),则删除expire_timer,并排队一个suspend工作到suspend_work_queue工作队列,最终系统会suspend
static long has_wake_lock_locked(int type) { struct wake_lock *lock, *n; long max_timeout = 0; BUG_ON(type >= WAKE_LOCK_TYPE_COUNT); list_for_each_entry_safe(lock, n, &active_wake_locks[type], link) { if (lock->flags & WAKE_LOCK_AUTO_EXPIRE) { long timeout = lock->expires - jiffies; if (timeout <= 0) expire_wake_lock(lock); else if (timeout > max_timeout) max_timeout = timeout; } else
return -1; } return max_timeout; }
expire_timer定义为: static DEFINE_TIMER(expire_timer, expire_wake_locks, 0, 0);
其handler expire_wake_locks是实现超时锁机制的关键,定时器的expire时间被设置为当前所有处于活动状态的WAKE_LOCK_SUSPEND锁超时值的最大值,如果没有超时锁则设置stop它。当定时器expire的时候,会在其处理函数expire_wake_locks中调用has_wake_lock_locked函数把所有过期的锁全部解锁,并排队一个suspend工作到suspend_work_queue工作队列,最终系统会suspend。
static void expire_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这个工作的处理函数,suspend_workk排队到suspend_work_queue工作队列中,最终系统会处理这个work,调用其handler即suspend函数。该函数首先sync文件系统,然后调用pm_suspend(request_suspend_state),接下来pm_suspend()就会调用 enter_state()来进入 linux的suspend流程。
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 (debug_mask & DEBUG_EXIT_SUSPEND) { struct timespec ts; struct rtc_time tm; getnstimeofday(&ts); rtc_time_to_tm(ts.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.tv_nsec); } if (current_event_num == entry_event_num) { if (debug_mask & DEBUG_SUSPEND) pr_info("suspend: pm_suspend returned with no event\n"); wake_lock_timeout(&unknown_wakeup, HZ / 2); } }
解锁由wake_unlock函数实现。该函数首先将该锁从active链表转移到inactive链表中。如果是WAKE_LOCK_IDLE锁,就结束退出了。如果是WAKE_LOCK_SUSPEND锁,则继续查看所有处于active状态并且具有自动过期属性的锁(超时锁),遍历找到最晚过期时间,然后修改expire_timer的到期时间(expire_timer到期后会调用suspend函数使系统进入suspend状态);否则,如果存在一个不具有auto-expire属性的锁(非超期锁),则会导致expire_timer被stop(或者说不再处于active的工作状态)。另外,如果检查的过程中发现所有锁均处于过期状态,则直接使用queue_work启动suspend过程。
userwakelock.c文件实现的是wakelock机制的sysfs 接口,用户可以通过这个接口操作锁,加锁或解锁。它通过struct user_wake_lock 结构体将所有的锁组织成红黑树的形式,树的根为user_wake_locks。
该文件是标准的sysfs接口函数,提供了wake_lock_show、wake_lock_store、wake_unlock_show 和wake_unlock_store四个函数,这样用户可以通过echo,cat等命令写入或读出系统中wake lock。
因为wakelock在实现的过程中,默认初始化并添加一个suspend lock类型的非过期型锁main_wake_lock(wakelocks_init 函数,wakelock.c)。因此,系统将始终因为main_wakelock的存在而正常运行。也就是说如果不添加新锁,将main_wake_lock 解锁后,系统将进入睡眠状态。
13.3 用户接口
电源管理内核层给应用层提供的接口就是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
命令系统进入earlysuspend状态,那些注册了early suspend handler的驱动将依次进入各自的earlysuspend 状态。
echo on > /sys/power/state
将退出early suspend状态
echo disk > /sys/power/state
命令系统进入hibernation状态
echo lockname > /sys/power/wake_lock
加锁“lockname”
echo lockname > /sys/power/wake_unlock
解锁“lockname”
上述是分别加锁和解锁的命令,一旦系统中所有wakelock被解锁,系统就会进入suspend状态,可见Android中原本使系统suspend 的操作(echo mem > /sys/power/state 等)被替换成使系统进入early suspend;而wake lock 机制成为用户命令系统进入suspend状态的唯一途径。

更多相关文章

  1. Android获取屏幕高度、标题高度、状态栏高度详解
  2. Android Button控件 的简单使用(button监听和onClick触发函数使用
  3. Android系统的架构
  4. android系统架构图
  5. Android底层开发之旅—蓝牙系统分析
  6. 史上最详细的Android系统SystemUI 启动过程详细解析
  7. Android App开发基础篇—64位Win10系统下安装配置开发环境
  8. Android系统信息获取 之四:系统语言信息获取

随机推荐

  1. NIPS2020 | 基于核的渐进式蒸馏的加法神
  2. CV学习笔记(三十一):人脸识别流程分析
  3. CV学习笔记(三十):交互式人脸活体检测
  4. CV学习笔记(二十九):活体检测总结②
  5. CV学习笔记(二十八):活体检测总结①
  6. CV学习笔记(二十七):Python Base64 格式图
  7. Exchange删除邮件
  8. CV学习笔记(二十六):NMS非极大值抑制算法
  9. CV学习笔记(二十五):数据集标注与制作
  10. 深度学习进阶NLP:word2vec的高速化