有四種方式可以引起休眠
①在wake_unlock()中, 如果發現解鎖以后沒有任何其他的wake lock了, 就開始休眠
②在定時器到時間以后, 定時器的回調函數會查看是否有其他的wake lock, 如果沒有, 就在這里讓系統進入睡眠
③在wake_lock() 中, 對一個wake lock加鎖以后, 會再次檢查一下有沒有鎖, 剛加上鎖,為什么要檢查,有用嗎?
④按power鍵,調用earlysuspend.使系統或應用程序釋放鎖.從而調用上述三個函數進入休眠
- earlysuspend、autosleep以及wakeup_count三種休眠機制的分析和比較
1) android 面臨的問題
Opportunistic sleep: 當沒有任務時,需要尋找時機,進入suspended
2) 3類同步問題
a. 內核:driver處理event的過程中,系統不能suspend
b. 用戶:用戶進程處理的過程中系統不能suspend
c. 內核與用戶交互:
休眠過程中,觸發event, 需abort suspend流程;
event 事件需用戶態處理完畢后,才能suspend;
2.常見的休眠喚醒機制有三種:earlysuspend、autosleep、wakeup_count。

-
earlysuspend機制介紹:

由上圖可得,應用程序application層創建,申請,釋放wake_lock鎖,通過上層應用類PowerManagerService.java獲得和釋放wake_lock鎖。
對於進入休眠,有兩種情況:
- 一種是power鍵滅屏關閉LCD、TP,或者后台有一些進程正在執行,如音樂播放器、文件傳輸之類,此時系統會先進入early_suspend狀態,然后當這些正在執行的進程執行完成,PowerManagerService檢查系統已經沒有wake_lock鎖時,系統進入深度休眠狀態,Early_suspend與late_resume成對出現。
-
另一種是,系統直接檢查到沒有wake_lock鎖時,直接進入深度睡眠狀態。
-
Autosleep

- Wakeup_count

-
Android在Linux內核原有的睡眠喚醒模塊基礎上,主要增加了下面三個機制
• Wake Lock 喚醒鎖機制;
• Early Suspend 預掛起機制;
• Late Resume 遲喚醒機制;
其基本原理:當啟動一個應用程序的時候,它可以申請一個wake_lock喚醒鎖,每當申請成功后都會在內核中注冊一下(通知系統內核,現在已經有鎖被申請,系統內核的wake_lock_store把它加入鎖中),當應用程序在某種情況下釋放wake_lock的時候,會注銷之前所申請的wake_lock。特別要注意的是:只要是系統中有一個wake_lock的時候,系統此時都不能進行睡眠。但此時各個模塊可以進行early_suspend。當系統中所有的wake_lock都被釋放之后,系統就會進入真正的kernel的睡眠狀態。在系統啟動的時候會創建一個主喚醒鎖main_wake_lock,該鎖是內核初始化並持有的一個WAKE_LOCK_SUSPEND屬性的非限時喚醒鎖。因此,系統正常工作時,將始終因為該鎖被內核持有而無法進入睡眠狀態。也就是說在不添加新鎖的情況下,只需將main_wake_lock 解鎖,系統即可進入睡眠狀態。
從Android最上層(Java的應用程序),經過Java、C++和C語言寫的Framework層、JNI層、HAL層最后到達android的最底層(Kernel層)。

相關代碼位置:
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 的用戶接口進行操作,從而觸發內核態實現的功能。
-
獲得wake_lock喚醒鎖
比如在應用程序中,當獲得wakelock喚醒鎖的時候,它首先調用/android/frameworks/base/core/java/android/os/PowerManager類中的public void acquire()辦法,而此方法通過Binder將調用PowerManagerService類中的public void acquireWakeLock。
在用戶態的調用流程如下:

上述write實質上是文件sysfs: /sys/power/wake_lock,當write時,它將調用userwakelock.c::wake_lock_store()函數,其實現如下:

1. 根據name在紅黑樹中查找user_wake_lock,若找到則直接返回;否則為它分配內存、調用wake_lock_init初始化、然后增加到紅黑樹中。
2. 調用wake_lock或wake_lock_timeout,它將調用wake_lock_internal
wake_lock_internal()函數流程:
1) 判斷鎖的類型是否有效,即是否為WAKE_LOCK_SUSPEND或WAKE_LOCK_IDLE某一種
2) 如果定義了CONFIG_WAKELOCK_STAT, 則更新struct wake_lock里面的用於統計鎖信息的成員變量
3) 將鎖從inactive_locks鏈表上取下,加到active_wake_locks鏈表上。如果是超期鎖則設置鎖的flag|=WAKE_LOCK_AUTO_EXPIRE,否則取消WAKE_LOCK_AUTO_EXPIRE標志。如果鎖是WAKE_LOCK_SUSPEND型的,則繼續下面的步驟。
4) 對於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,否則它最終返回的是所有超期鎖過期時間的最大值
5) 如果has_wake_lock_locked函數返回的是-1(表示當前活動鎖有非超時鎖)或者0(表示所有活動鎖都是超時鎖,且全已經超時),則刪除expire_timer,並排隊一個suspend工作到suspend_work_queue工作隊列,最終系統會suspend。suspend函數完成suspend系統的任務,它是suspend_work這個工作的處理函數,suspend_workk排隊到suspend_work_queue工作隊列中,最終系統會處理這個work,調用其handler即suspend函數。該函數首先sync文件系統,然后調用pm_suspend(request_suspend_state),接下來pm_suspend()就會調用 enter_state()來進入 linux的suspend流程。
-
系統進入休眠(suspend)
當按了MID上的power鍵,經過一系統列的事務處理后,它會調用到PowerManager類中的goToSleep。在用戶態的調用流程如下所示:

最后一步write()寫操作把"mem"寫入/sys/power/state中。接下來,在kernel態,state_store函數將被調用。
• Android特有的earlysuspend: request_suspend_state(state)
• Linux標准的suspend: enter_state(state)
在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狀態。
| 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;
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 #endif #endif Exit: return error ? error : n; } |
在request_suspend_state(state)函數中,會調用early_suspend_work的工作隊列,從而進入early_suspend()函數中。
static DECLARE_WORK(early_suspend_work, 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-d-d d:d:d. lu 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()函數中,首先要判斷當前請求的狀態是否還是suspend,若不是,則直接退出了;若是,函數會調用已經注冊的early_suspend的函數。然后同步文件系統,最后釋放main_wake_lock。
|
在suspend()函數中,先判斷當前是否有wake_lock,若有,則退出;然后同步文件系統,最后調用pm_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-d-d d:d:d. lu 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); } } |
在pm_suspend()函數中,enter_state()函數被調用,從而進入標准linux休眠過程
| int pm_suspend(suspend_state_t state) { if (state > PM_SUSPEND_ON && state <= PM_SUSPEND_MAX) return enter_state(state); return -EINVAL; } |
在enter_state()函數中,首先檢查一些狀態參數,再同步文件系統,然后調用suspend_prepare()來凍結進程,最后調用suspend_devices_and_enter()讓外設進入休眠。
| static int enter_state(suspend_state_t state) { int error;
if (!valid_state(state)) return -ENODEV;
if (!mutex_trylock(&pm_mutex)) return -EBUSY;
printk(KERN_INFO "PM: Syncing filesystems ... "); sys_sync(); printk("done.\n");
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); error = suspend_prepare(); if (error) goto Unlock;
if (suspend_test(TEST_FREEZER)) goto Finish;
pr_debug("PM: Entering %s sleep\n", pm_states[state]); error = suspend_devices_and_enter(state);
Finish: pr_debug("PM: Finishing wakeup.\n"); suspend_finish(); Unlock: mutex_unlock(&pm_mutex); return error; } 外部設備在注冊時,都會注冊其自身的suspend函數,enter_state()函數通過回調這些設備的suspend使其進入suspend態 |
-
Linux電源管理的基礎培訓

- 高通平台的電源管理機制是由RPM(running PowerManager)統一管理,每個子系統都有自己的休眠喚醒機制,當子系統進入待機時,其會發送消息給RPM,並讓自身進入待機狀態,RPM子系統根據各子系統的休眠狀態,決定整個系統是否進待機(XO_SHUTDOWN)
- PMIC有單獨的電源給子系統供電,這就決定了子系統待機時可以關閉自身的電源用於降低功耗。
-
AP的待機有幾種模式:
- WFI:wait for interrupt:正常的待機狀態,AP電源關閉,GIC模塊工作,用於接收中斷
- Idle power collapse: 正常工作時CPU進低功耗模式
- 子系統的待機不受其他子系統的影響
-
AP休眠
AP的休眠喚醒流程由應用層發起,統一由power manager service線程管理,其通過JNI調用libsuspend的庫函數,對內核進行操作。 Powermanger service: /frameworks/base/service/core/java/com/android/server/power/powermanagerservice.java JNI: /frameworks/base/service/core/jni Libsuspend: /system/core/libsuspend內核的接口 Linux內核有兩種休眠喚醒接口: /sys/power/autosleep /sys/power/state 其區別在autosleep的方式為當應用設置系統休眠時,其如果發現無法進待機,則會不斷的嘗試進行待機。State方式為用戶空間設置系統待機,如果無法待機,則退出待機流程。 Autosleep方式目前默認不采用,主要原因是應用的控制流程上有無法規避的問題。
-
每個模塊都會注冊其自身的休眠喚醒流程,用於進行低功耗處理


對於系統核心模塊的休眠是在最后一步syscore suspend中完成,如cpu、GIC、CLK、SMD的休眠處理。 
