Android电源管理-休眠简要分析

来源:互联网 发布:手机支付软件排名 编辑:程序博客网 时间:2024/05/29 12:23

工作需要,需要对这一块深入学习。故在此做一点分析记录,存疑解惑。

一、开篇

 1.Linux 描述的电源状态

 - On(on)                                                 S0 -  Working

- Standby (standby)                              S1 -  CPU and RAM are powered but not executed

- Suspend to RAM(mem)                        S3 -  RAM is powered and the running content is saved to RAM

- Suspend to Disk,Hibernation(disk)    S4 -  All content is saved to Disk and power down

 

S3 aka STR(suspend to ram),挂起到内存,简称待机。计算机将目前的运行状态等数据存放在内存,关闭硬 盘、外设等设备,进入等待状态。此时内存仍然需要电力维持其数据,但整机耗电很少。恢复时计算机从内存读出数据,回到挂起前的状态,恢复速度较快。对 DDR的耗电情况进行优化是S3性能的关键,大多数手持设备都是用S3待机。

S4 aka STD(suspend to disk),挂起到硬盘,简称休眠。把运行状态等数据存放在硬盘上某个文件或者某个特定的区域,关闭硬盘、外设等设备,进入关机状态。此时计算机完全关闭,不耗电。恢复时计算机从休眠文件/分区中读出数据,回到休眠前的状态,恢复速度较慢。电子书项目中,见过一款索尼的电子书,没有定义关机状态,只定义了S4,从而提高开机速度。

 

以上摘录自:http://cache.baiducontent.com/c?m=9f65cb4a8c8507ed4fece763104

6893b4c4380147d8c8c4668d4e419ce3b4c413037bfa6663f405a8e906b6075fc

4d5bedfb6079370123b598938f4a85ac925f75ce786a6459db0144dc5bf0dc475

5d627e44de8df4aa0fcad7384afa28d880311dd52756d87849c5b704f9634b6&p

=c933cc16d9c116f51ebd9b7d0a13cd&newp=8366c54ad5c444e411b3c22d0214cf2

31610db2151d6db10349dcd1e&user=baidu&fm=sc&query=pm_autosleep_init&qid=&p1=1

 

在阅读下面的内容之前,强烈建议阅读下。

Android在Linux内核原有的睡眠唤醒机制上面新增了三个,如下:

     • Wake Lock 唤醒锁机制;
     • Early Suspend 预挂起机制;
     • Late Resume 迟唤醒机制;

我们来看一张Android睡眠唤醒机制的框架图:

      • Android特有的earlysuspend: request_suspend_state(state)
      • Linux标准的suspend:       enter_state(state)

二、相关代码涉及文件

    • Frameworks

      // 供给上层应用程序调用的接口
      frameworks/base/core/java/android/os/PowerManager.java 

      // 具体实现PowerManager类中的接口
      frameworks/base/services/java/com/android/server/PowerManagerService.java        

      // 被PowerManagerService类调用

      frameworks/base/core/java/android/os/ Power.java

    • JNI

     // 实现Power类中的JNI接口
     frameworks/base/core/jni/android_os_Power.cpp

    • HAL

      // 进行sysfs用户接口的操作
      hardware/libhardware_legacy/power/power.c

    • Kernel

      kernel/kernel/power/main.c 
      kernel/kernel/power/earlysuspend.c
      kernel/kernel/power/suspend.c
      kernel/kernel/power/wakelock.c
      kernel/kernel/power/userwakelock.c

  在应用程序框架层中,PowerManager类是面向上层应用程序的接口类,提供了Wake Lock机制(同时也是睡眠唤醒子系统)的基本接口(唤醒锁的获取和释放)。上层应用程序通过调用这些接口,实现对系统电源状态的监控。

     • PowerManager类通过IBinder这种Android中特有的通信模式,与PowerManagerService 类进行通信。  

     • PowerManagerService 是PowerManager 类中定义的接口的具体实现,并进一步调用Power 类来与下一层进行通信。PowerManagerService 类是WakeLock 机制在应用程序框架层的核心,他们对应用程调用PowerManager类接口时所传递的参数进行初步的分析和对应的设置,并管理一个唤醒锁队列,然后配合其他模块(例如WatchDog、BatteryService、ShutdownThread 等)的状态信息,做出决策,调用Power类的对应接口,最终通过JNI 接口,调用到硬件抽象层中的函数,对sysfs 的用户接口进行操作,从而触发内核态实现的功能。

三、Kernel用户空间接口分析

1. sysfs的属性文件

电源管理内核层给应用层提供的接口就是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状态,可见Linux中原本使系统 suspend 的操作(echo mem > /sys/power/state 等)在Android被替换成使系统进入early suspend;而wake lock 机制成为用户命令系统进入suspend状态的唯一途径。

Kernel与HAL接口是通过/sys/power下面的一系统文件来实现的,如:/sys/power/state.在用户空间接口中,定义了一组sysfs的属性文件,其中一个定义是:

1 power_attr(state)

这个宏的代码如下:

复制代码
1 #define power_attr(_name) \2 static struct kobj_attribute _name##_attr = {    \3     .attr    = {                \4         .name = __stringify(_name),    \5         .mode = 0644,            \6     },                    \7     .show    = _name##_show,            \8     .store    = _name##_store,        
复制代码

展开后的代码是:

复制代码
1 #define power_attr(state) \2 static struct kobj_attribute state_attr = {    \3     .attr    = {                \4         .name = "state",    \5         .mode = 0644,            \6     },                    \7     .show    = state_show,            \8     .store    = state_store,        
复制代码

2. 创建sysfs文件

复制代码
 1 static int __init pm_init(void) 2 { 3     int error = pm_start_workqueue(); 4     if (error) 5         return error; 6     hibernate_image_size_init(); 7     hibernate_reserved_size_init(); 8     power_kobj = kobject_create_and_add("power", NULL); 9     if (!power_kobj)10         return -ENOMEM;11     error = sysfs_create_group(power_kobj, &attr_group);  //创建sys文件接口12     if (error)13         return error;14     pm_print_times_init();15     return pm_autosleep_init();  //创建auto_sleep工作队列,也把用户态向autosleep 写入当作wakeup_source16 }17 18 core_initcall(pm_init)  //调用pm_init
复制代码

 pm_init函数执行后,会创建/sys/power目录,且目录下会建立一系列属性文件,其中一个是/sys/power/state文件。用户空间写该文件将会导致state_store被调用,读该文件将会导致state_show函数被调用。

2. 标准的Linux内核调用suspend流程

复制代码
 1 static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr, 2                const char *buf, size_t n) 3 { 4     suspend_state_t state; 5     int error; 6  7     error = pm_autosleep_lock(); 8     if (error) 9         return error;10 11     if (pm_autosleep_state() > PM_SUSPEND_ON) {  // autosleep是android内核为了跟主线内核兼容所引入的12         error = -EBUSY;13         goto out;14     }15 16     state = decode_state(buf, n);17     if (state < PM_SUSPEND_MAX)18         error = pm_suspend(state);          // 进入suspend 的模式19     else if (state == PM_SUSPEND_MAX)20         error = hibernate();              // 进入冬眠模式21     else22         error = -EINVAL;23 24  out:25     pm_autosleep_unlock();26     return error ? error : n;
复制代码

 当底层接受到上层传递到的值进行一些列的操作,有很多的state 状态:

1 #define PM_SUSPEND_ON        ((__force suspend_state_t) 0)  // S02 #define PM_SUSPEND_STANDBY    ((__force suspend_state_t) 1)  // S13 #define PM_SUSPEND_MEM        ((__force suspend_state_t) 3)  // S24 #define PM_SUSPEND_MAX        ((__force suspend_state_t) 4)   // S3

在state_store中,若定义了CONFIG_EARLYSUSPEND,则执行 request_suspend_state(state)以先进入earlysuspend,然后根据wake_lock的状态决定是否进入 suspend;否则直接执行enter_state(state)以进入suspend状态。我们来看下pm_suspend的原生代码:

复制代码
 1 int pm_suspend(suspend_state_t state) 2 { 3     int error; 4  5     if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)  // state参数无效 6         return -EINVAL; 7  8     pm_suspend_marker("entry"); 9     error = enter_state(state);10     if (error) {11         suspend_stats.fail++;12         dpm_save_failed_errno(error);13     } else {14         suspend_stats.success++;15     }16     pm_suspend_marker("exit");17     return error;
复制代码

 

三、Android 休眠(suspend)

1. 相关文件
     • kernel/kernel/power/main.c
     • kernel/kernel/power/earlysuspend.c
     • kernel/kernel/power/wakelock.c

 

2. 特性介绍
    1) Early Suspend
       Early suspend 是android 引进的一种机制,这种机制在上游备受争议,这里不做评论。 这个机制作用是在关闭显示的时候,一些和显示有关的设备,比如LCD背光、重力感应器、 触摸屏都会关掉,但是系统可能还是在运行状态(这时候还有wake lock)进行任务的处理,例如在扫描 SD卡上的文件等。 在嵌入式设备中,背光是一个很大的电源消耗,所以android会加入这样一种机制。


     2) Late Resume
         Late Resume 是和suspend 配套的一种机制,是在内核唤醒完毕开始执行的。主要就是唤醒在Early Suspend时休眠的设备。


     3) Wake Lock
         wake_lock 在Android的电源管理系统中扮演一个核心的角色。wake_lock是一种锁的机制,只要有人拿着这个锁,系统就无法进入休眠,可以被用户态程序和 内核获得。这个锁可以是有超时的或者是没有超时的,超时的锁会在超时以后自动解锁。如果没有锁了或者超时了,内核就会启动休眠的那套机制来进入休眠。

3. Android Suspend
      main.c文件是整个框架的入口。用户可以通过读写sys文件/sys/power/state实现控制系统进入低功耗状态。用户对于/sys /power/state的读写会调用到main.c中的state_store(),用户可以写入const char * const pm_states[] 中定义的字符串, 比如“on”,“mem”,“standby”,“disk”。 

1 const char *const pm_states[PM_SUSPEND_MAX] = {2     [PM_SUSPEND_FREEZE]    = "freeze",3     [PM_SUSPEND_STANDBY]    = "standby",4     [PM_SUSPEND_MEM]    = "mem",5 }

      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()函数代码如下:

 View Code

 

 

 TAG:

复制代码
1 const char * const OLD_PATHS[] = {2     "/sys/android_power/acquire_partial_wake_lock",3     "/sys/android_power/release_wake_lock",4 };5 6 const char * const NEW_PATHS[] = {7     "/sys/power/wake_lock",8     "/sys/power/wake_unlock",9 };
复制代码
复制代码
 1 static inline void 2 initialize_fds(void) 3 { 4     // XXX: should be this: 5     //pthread_once(&g_initialized, open_file_descriptors); 6     // XXX: not this: 7     if (g_initialized == 0) { 8         if(open_file_descriptors(NEW_PATHS) < 0) 9             open_file_descriptors(OLD_PATHS);10         g_initialized = 1;11     }
复制代码

 

未完待续......

 

本文很多内容参考且摘录自:

http://blog.csdn.net/myarrow/article/details/8136691

http://blog.csdn.net/myarrow/article/details/8137952

http://blog.csdn.net/myarrow/article/details/8137566

http://blog.csdn.net/sunweizhong1024/article/details/17102047

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 新手眉毛修坏了怎么办 小麦补贴卡丢了怎么办 种粮大户没粮仓怎么办? 公司不发高温费怎么办 低保户没低保证怎么办 鸿运当头花干了怎么办 鸿运当头花谢了怎么办 鸿运当头花不红怎么办 众筹项目失败了怎么办 孕妇头发白了怎么办呢 猕猴桃坏了吃了怎么办 猕猴桃买的太硬怎么办 金艳猕猴桃很硬怎么办 猕猴桃又硬又酸怎么办 2岁宝宝生长缓慢怎么办 婴儿体重长得慢怎么办 人吃了乌药中毒怎么办 水中的氧气过少怎么办 怀孕9个月血糖高怎么办 怀孕的人血糖高怎么办 来月经很痛该怎么办 月经来了好痛怎么办 花呗额度用完了怎么办 老人长期卧床长了褥疮怎么办 5岁宝宝嗓子哑了怎么办 2岁宝宝喉咙嘶哑怎么办 1岁宝宝嗓子哑了怎么办 3个月婴儿嗓子哑怎么办 小孩一感冒就喘怎么办 狗打双黄连过敏怎么办 过敏怎么办怎么好的快 铁木砧板变形了怎么办 水泥地面起沙严重怎么办 横厅沙发不靠墙怎么办 吃多了胃疼怎么办 龙血树叶子干枯怎么办 龙血树叶尖发黄怎么办 茶花叶子掉光了怎么办 鸡虱子爬人身上怎么办 鸡的身上有虱子怎么办 鸡跳蚤在人身上怎么办