Android睡眠喚醒機制–系統架構

來源:互聯網
上載者:User

一、簡介

     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層)。

   

     是Android睡眠喚醒模組架構:

 二、相關代碼

    • 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 的使用者介面進行操作,從而觸發核心態實現的功能。

 三、獲得wakelock喚醒鎖
      比如在應用程式中,當獲得wakelock喚醒鎖的時候,它首先調用/android/frameworks/base/core/java/android/os/PowerManager類中的public void acquire()辦法,而此方法通過Binder將調用PowerManagerService類中的public void acquireWakeLock。

     在使用者態的調用流程如下:

PowerManager.acquire()-> PowerManager.acquireLocked()->  PowerManagerService.acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws)->   PowerManagerService.acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid,                                              String tag, WorkSource ws)->    Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME)->     android_os_Power.cpp::acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj)->      power.c::acquire_wake_lock(int lock, const char* id)->       write(fd, id, strlen(id))

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

ssize_t wake_lock_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t n){long timeout;struct user_wake_lock *l;mutex_lock(&tree_lock);l = lookup_wake_lock_name(buf, 1, &timeout);if (IS_ERR(l)) {n = PTR_ERR(l);goto bad_name;}if (debug_mask & DEBUG_ACCESS)pr_info("wake_lock_store: %s, timeout %ld\n", l->name, timeout);if (timeout)wake_lock_timeout(&l->wake_lock, timeout);elsewake_lock(&l->wake_lock);bad_name:mutex_unlock(&tree_lock);return n;}

   1. 根據name在紅/黑樹狀結構中尋找user_wake_lock,若找到則直接返回;否則為它分配記憶體、調用wake_lock_init初始化、然後增加到紅/黑樹狀結構中。

   2. 調用wake_lock或wake_lock_timeout,它將調用wake_lock_internal

static void wake_lock_internal(struct wake_lock *lock, long timeout, int has_timeout){int type;unsigned long irqflags;long expire_in;spin_lock_irqsave(&list_lock, irqflags);type = lock->flags & WAKE_LOCK_TYPE_MASK;BUG_ON(type >= WAKE_LOCK_TYPE_COUNT);BUG_ON(!(lock->flags & WAKE_LOCK_INITIALIZED));#ifdef CONFIG_WAKELOCK_STATif (type == WAKE_LOCK_SUSPEND && wait_for_wakeup) {if (debug_mask & DEBUG_WAKEUP)pr_info("wakeup wake lock: %s\n", lock->name);wait_for_wakeup = 0;lock->stat.wakeup_count++;}if ((lock->flags & WAKE_LOCK_AUTO_EXPIRE) &&    (long)(lock->expires - jiffies) <= 0) {wake_unlock_stat_locked(lock, 0);lock->stat.last_time = ktime_get();}#endifif (!(lock->flags & WAKE_LOCK_ACTIVE)) {lock->flags |= WAKE_LOCK_ACTIVE;#ifdef CONFIG_WAKELOCK_STATlock->stat.last_time = ktime_get();#endif}list_del(&lock->link);if (has_timeout) {if (debug_mask & DEBUG_WAKE_LOCK)pr_info("wake_lock: %s, type %d, timeout %ld.%03lu\n",lock->name, type, timeout / HZ,(timeout % HZ) * MSEC_PER_SEC / HZ);lock->expires = jiffies + timeout;lock->flags |= WAKE_LOCK_AUTO_EXPIRE;list_add_tail(&lock->link, &active_wake_locks[type]);} else {if (debug_mask & DEBUG_WAKE_LOCK)pr_info("wake_lock: %s, type %d\n", lock->name, type);lock->expires = LONG_MAX;lock->flags &= ~WAKE_LOCK_AUTO_EXPIRE;list_add(&lock->link, &active_wake_locks[type]);}if (type == WAKE_LOCK_SUSPEND) {current_event_num++;#ifdef CONFIG_WAKELOCK_STATif (lock == &main_wake_lock)update_sleep_wait_stats_locked(1);else if (!wake_lock_active(&main_wake_lock))update_sleep_wait_stats_locked(0);#endifif (has_timeout)expire_in = has_wake_lock_locked(type);elseexpire_in = -1;if (expire_in > 0) {if (debug_mask & DEBUG_EXPIRE)pr_info("wake_lock: %s, start expire timer, ""%ld\n", lock->name, expire_in);mod_timer(&expire_timer, jiffies + expire_in);} else {if (del_timer(&expire_timer))if (debug_mask & DEBUG_EXPIRE)pr_info("wake_lock: %s, stop expire timer\n",lock->name);if (expire_in == 0)queue_work(suspend_work_queue, &suspend_work);}}spin_unlock_irqrestore(&list_lock, irqflags);}

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。在使用者態的調用流程如下所示: 

PowerManager.goToSleep(long time)-> PowerManagerService.goToSleep(long time)->  PowerManagerService.goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER)->   PowerManagerService.goToSleepLocked(long time, int reason)->    PowerManagerService.setPowerState(SCREEN_OFF, false, reason)->     PowerManagerService.sendNotificationLocked(false, WindowManagerPolicy.OFF_BECAUSE_OF_USER)      PowerManagerService.setScreenStateLocked(true)->       Power.setScreenState->        android_os_Power.cpp::setScreenState->         Power.c::set_screen_state(int on)->          write(g_fds[REQUEST_STATE], buf, len)

      上面的write將把"mem"寫入/sys/power/state中。接下來,在kernel態,state_store函數將被調用。

      • Android特有的earlysuspend: request_suspend_state(state)
      • Linux標準的suspend:       enter_state(state)

      在state_store中,若定義了CONFIG_EARLYSUSPEND,則執行request_suspend_state(state)以先進入earlysuspend,然後根據wake_lock的狀態決定是否進入suspend;否則直接執行enter_state(state)以進入suspend狀態。

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.