Android電源管理-休眠簡要分析


工作需要,需要對這一塊深入學習。故在此做一點分析記錄,存疑解惑。

一、開篇

 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_source
16 }
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     else
22         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)  // S0
2 #define PM_SUSPEND_STANDBY    ((__force suspend_state_t) 1)  // S1
3 #define PM_SUSPEND_MEM        ((__force suspend_state_t) 3)  // S2
4 #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()函數代碼如下:

 1 void request_suspend_state(suspend_state_t new_state)
 2 {
 3     unsigned long irqflags;
 4     int old_sleep;
 5 
 6 #ifdef CONFIG_PLAT_RK
 7     if (system_state != SYSTEM_RUNNING)
 8         return;
 9 #endif
10 
11     spin_lock_irqsave(&state_lock, irqflags);
12     old_sleep = state & SUSPEND_REQUESTED;
13     if (debug_mask & DEBUG_USER_STATE) {
14         struct timespec ts;
15         struct rtc_time tm;
16         getnstimeofday(&ts);
17         rtc_time_to_tm(ts.tv_sec, &tm);
18         pr_info("request_suspend_state: %s (%d->%d) at %lld "
19             "(%d-%02d-%02d %02d:%02d:%02d.%09lu UTC)\n",
20             new_state != PM_SUSPEND_ON ? "sleep" : "wakeup",
21             requested_suspend_state, new_state,
22             ktime_to_ns(ktime_get()),
23             tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
24             tm.tm_hour, tm.tm_min, tm.tm_sec, ts.tv_nsec);
25     }
26     if (!old_sleep && new_state != PM_SUSPEND_ON) {
27         state |= SUSPEND_REQUESTED;
28                   //進入Early suspend處理,執行函數early_suspend
29         queue_work(suspend_work_queue, &early_suspend_work);
30     } else if (old_sleep && new_state == PM_SUSPEND_ON) {
31         state &= ~SUSPEND_REQUESTED;
32         wake_lock(&main_wake_lock);
33                   //進入Late resume處理,執行函數late_resume
34         queue_work(suspend_work_queue, &late_resume_work);
35     }
36     requested_suspend_state = new_state;
37     spin_unlock_irqrestore(&state_lock, irqflags);
38 }
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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM