Android休眠喚醒機制


有四種方式可以引起休眠

        ①在wake_unlock()中, 如果發現解鎖以后沒有任何其他的wake lock了, 就開始休眠

        ②在定時器到時間以后, 定時器的回調函數會查看是否有其他的wake lock, 如果沒有, 就在這里讓系統進入睡眠

        ③在wake_lock() 中, 對一個wake lock加鎖以后, 會再次檢查一下有沒有鎖,  剛加上鎖,為什么要檢查,有用嗎?

        ④按power鍵,調用earlysuspend.使系統或應用程序釋放鎖.從而調用上述三個函數進入休眠

  1. 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。

  1. earlysuspend機制介紹:

    由上圖可得,應用程序application層創建,申請,釋放wake_lock鎖,通過上層應用類PowerManagerService.java獲得和釋放wake_lock鎖。

    對於進入休眠,有兩種情況:

    1. 一種是power鍵滅屏關閉LCD、TP,或者后台有一些進程正在執行,如音樂播放器、文件傳輸之類,此時系統會先進入early_suspend狀態,然后當這些正在執行的進程執行完成,PowerManagerService檢查系統已經沒有wake_lock鎖時,系統進入深度休眠狀態,Early_suspend與late_resume成對出現。
    2. 另一種是,系統直接檢查到沒有wake_lock鎖時,直接進入深度睡眠狀態。

       

  2. Autosleep

  3. Wakeup_count

 

  1. 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 的用戶接口進行操作,從而觸發內核態實現的功能。

  2. 獲得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流程。

     

     

  3. 系統進入休眠(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

error = enter_state(state);

#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

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

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

}

wake_unlock()中,刪除鏈表中wake_lock節點,判斷當前是否存在wake_lock,若wake_lock的數目為0,則調用工作隊列suspend_work,進入suspend狀態。

tatic DECLARE_WORK(suspend_work, suspend);

void wake_unlock(struct wake_lock *lock)

{

int type;

unsigned long irqflags;

spin_lock_irqsave(&list_lock, irqflags);

type = lock->flags & WAKE_LOCK_TYPE_MASK;

#ifdef CONFIG_WAKELOCK_STAT

wake_unlock_stat_locked(lock, 0);

#endif

if (debug_mask & DEBUG_WAKE_LOCK)

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

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

list_del(&lock->link);

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

if (type == WAKE_LOCK_SUSPEND) {

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

mod_timer(&expire_timer, jiffies + has_lock);

} else {

if (del_timer(&expire_timer))

if (debug_mask & DEBUG_EXPIRE)

pr_info("wake_unlock: %s, stop expire "

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

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

#ifdef CONFIG_WAKELOCK_STAT

update_sleep_wait_stats_locked(0);

#endif

}

}

spin_unlock_irqrestore(&list_lock, irqflags);

}

在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態

 

  1. Linux電源管理的基礎培訓

     

    1. 高通平台的電源管理機制是由RPM(running PowerManager)統一管理,每個子系統都有自己的休眠喚醒機制,當子系統進入待機時,其會發送消息給RPM,並讓自身進入待機狀態,RPM子系統根據各子系統的休眠狀態,決定整個系統是否進待機(XO_SHUTDOWN)
    2. PMIC有單獨的電源給子系統供電,這就決定了子系統待機時可以關閉自身的電源用於降低功耗。
    3. AP的待機有幾種模式:
      1. WFI:wait for interrupt:正常的待機狀態,AP電源關閉,GIC模塊工作,用於接收中斷
      2. Idle power collapse: 正常工作時CPU進低功耗模式
    4. 子系統的待機不受其他子系統的影響
  2. 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方式目前默認不采用,主要原因是應用的控制流程上有無法規避的問題。

     

  3. 每個模塊都會注冊其自身的休眠喚醒流程,用於進行低功耗處理

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


免責聲明!

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



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