Android/linux(earlysuspend、lateresume)睡眠喚醒機制簡介

本文屬於原創!!如要轉載,請註明來源處 http://blog.sina.com.cn/s/blog_759dc36b0100stax.html

  背景介紹:

睡眠/喚醒嵌入式Linux非常重要的組成部分,因爲優秀的睡眠喚醒機制可以是嵌入式設備儘可能的進入休眠狀態來延長電池的續航時間(這在移動終端消費類電子設備中是非常重要和有意義的!!)。但標準的Linux睡眠喚醒機制有其自身的一些缺陷(所有模塊必須同時睡下或者喚醒),在某些情況下,這會導致能耗的白白浪費。因此Android在標準Linux睡眠喚醒的機制上作了新的改動(wake_lock喚醒、early_suspend和late_resume機制),從而很好的解決上面的問題。Android2.3.1版本爲例,詳細介紹標準Linux睡眠/喚醒是如何工作的, 並且Android中如何把其自身特有的機制和Linux中標準的聯繫起來的

  標準Linux睡眠喚醒機制簡介:

    在標準Linux中,休眠主要分三個主要的步驟:(1)凍結用戶態進程和內核態任務;(2)調用註冊的設備的suspend的回調函數,其調用順序是按照驅動加載時的註冊順序。(3)休眠核心設備和使CPU進入休眠態凍結進程是內核把進程列表中所有的進程的狀態都設置爲停止,並且保存下所有進程的上下文。 當這些進程被解凍的時候,它們是不知道自己被凍結過的,只是簡單的繼續執行。

    那麼是如何讓Linux進入休眠的呢?其實很簡單,因爲Androidkernel已經做了很多複雜的工作,所以用戶只需可以通過讀寫sys文件/sys /power/state 就可以實現控制系統進入休眠。 

比如: echo  mem  /sys/power/state               使系統進行睡眠

       # echo  on   /sys/power/state     使系統從睡眠中喚醒過來

當然還有其它的狀態操作,在下面的內容中將有介紹。

  Android睡眠喚醒機制簡介:

AndroidLinux內核原有的睡眠喚醒模塊上基礎上,主要增加了下面三個機制:

    Wake _Lock 喚醒鎖機制;

Early _Suspend 預掛起機制;

Late _Resume 遲喚醒機制;

其基本原理如下:當啓動一個應用程序的時候,它都可以申請一個wake_lock喚醒鎖,每當申請成功之後都會在內核中註冊一下(通知系統內核,現在已經有鎖被申請),當應用程序在某種情況下釋放wake_lock的時候,會註銷之前所申請的wake_lock特別要注意的是:只要是系統中有一個wake_lock的時候,系統此時都不能進行睡眠。但此時各個模塊可以進行early_suspend當系統中所有的wake_lock都被釋放之後,系統就會進入真正的kernel的睡眠狀態。在系統啓動的時候會創建一個主喚醒鎖main_wake_lock該鎖是內核初始化並持有的一個WAKE_LOCK_SUSPEND屬性的非限時喚醒鎖。因此,系統正常工作時,將始終因爲該鎖被內核持有而無法進入睡眠狀態。也就是說在不添加新鎖的情況下,只需將main_wake_lock 解鎖,系統即可進入睡眠狀態。    

    下面是Android睡眠喚醒模塊框架(該圖引用上屆師兄畢設裏圖片,自己懶得再畫了,這就是魯迅所說的“拿來主義” ^):


    接下來我們將以上圖的框架結構爲主線,將進行非常非常詳細地從最上層到最底層的跟蹤!!!本文的主旨主要就是讀者從Android最上層(Java寫的應用程序)一步一步的往下跟進,經過JavaC++C語言寫的Framework層、JNI層、HAL層最後到達android的最底層(Kernel層)。通過本文的閱讀,您將對android的整體有更加深入、宏觀的理解和把握!

   主要涉及到的目錄文件:

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

android/frameworks/base/services/java/com/android/server/PowerManagerService.java

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

android/frameworks/base/core/jni/android_os_Power.cpp

android/hardware/libhardware_legacy/power/power.c

 

android/kernel/kernel/power/main.c 

android/kernel/kernel/power/earlysuspend.c

android/kernel/kernel/power/suspend.c

android/kernel/kernel/power/wakelock.c

android/kernel/kernel/power/userwakelock.c

    在應用程序框架層中,PowerManager類是面向上層應用程序的接口類,提供了Wake Lock機制(同時也是睡眠喚醒子系統)的基本接口(喚醒鎖的獲取和釋放)。上層應用程序通過調用這些接口,實現對系統電源狀態的監控。PowerManager類通過IBinder這種Android中特有的通信模式,與PowerManagerService 類進行通信。PowerManagerService PowerManager 類中定義的接口的具體實現,並進一步調用Power 類來與下一層進行通信。PowerManagerService 類是WakeLock 機制在應用程序框架層的核心,他們對應用程調用PowerManager類接口時所傳遞的參數進行初步的分析和對應的設置,並管理一個喚醒鎖隊列,然後配合其他模塊(例如WatchDogBatteryServiceShutdownThread 等)的狀態信息,做出決策,調用Power類的對應接口,最終通過JNI 接口,調用到硬件抽象層中的函數,對sysfs 的用戶接口進行操作,從而觸發內核態實現的用。 (該段引自於上屆一個師兄的畢設原文,在這就直接用了,希望師兄看了不要介意了哈 ^

    PowerManager.java提供上層應用程序的接口;

    PowerManagerService.java具體實現PowerManager類中的接口;

    Power.javaPowerManagerService類調用;

    android_os_Power.cpp實現Power類中的JNI接口;

    power.c進行sysfs用戶接口的操作。

其餘涉及到的都是內核kernel中的文件,它們的作用將在下面給予介紹。

   具體流程:

下面我將分別以兩條路線(第一:獲得wakelock喚醒鎖。第二:系統進入睡眠。)來分別說明各自的流程,讓讀者對android睡眠喚醒機制有更深入的理解!

   第一部分:獲得wakelock喚醒鎖

    比如在應用程序中,當獲得wakelock喚醒鎖的時候,它首先是調用/android/frameworks/base/core/java/

android/os/PowerManager類中的public void acquire()方法,而該方法通過android特有的通訊機制,會接着調用到PowerManagerService類中的public void acquireWakeLock

public void acquireWakeLock(int flags, IBinder lock, String tag, WorkSource ws) {

        int uid Binder.getCallingUid();

        int pid Binder.getCallingPid();

        if (uid != Process.myUid()) {

            mContext.enforceCallingOrSelfPermission(android.Manifest.permission.WAKE_LOCK, null);

           

        if (ws != null) {

            enforceWakeSourcePermission(uid, pid);

           

        long ident Binder.clearCallingIdentity();

        try {

            synchronized (mLocks) {

                acquireWakeLockLocked(flags, lock, uid, pid, tag, ws); 

               

        finally {

            Binder.restoreCallingIdentity(ident);

           

  

而 public void acquireWakeLock方法又調用了acquireWakeLockLocked

public void acquireWakeLockLocked(int flags, IBinder lock, int uid, int pid, String tag,

            WorkSource ws)

 {

    if (mSpew) {

     Slog.d(TAG, "acquireWakeLock flags=0x" Integer.toHexString(flags) tag=" tag); }

        if (ws != null && ws.size() == 0) {ws null;}

        int index mLocks.getIndex(lock);

        WakeLock wl;

        boolean newlock;

        boolean diffsource;

        WorkSource oldsource;

                      

                      

                      

                 中間代碼省略

                      

                      

                      

          Power.acquireWakeLock(Power.PARTIAL_WAKE_LOCK,PARTIAL_NAME);

        }

        if (diffsource) {

            // If the lock sources have changed, need to first release the

            // old ones.

            noteStopWakeLocked(wl, oldsource);

        }

        if (newlock || diffsource) {

            noteStartWakeLocked(wl, ws);

        }

}

我們可以看到在acquireWakeLockLocked 方法調用Power中的public static native void acquireWakeLock(int lock, String id)方法。而該方法是調用android_os_Power.cpp中的static void acquireWakeLock()函數。

static void acquireWakeLock(JNIEnv *env, jobject clazz, jint lock, jstring idObj)

{

    if (idObj == NULL) {

        throw_NullPointerException(env, "id is null");

        return ;

    }

    const char *id env->GetStringUTFChars(idObj, NULL);

    acquire_wake_lock(lock, id);

    env->ReleaseStringUTFChars(idObj, id);

}

    函數 acquire_wake_lock()的實現在 power.c中,其定義如下:

int  acquire_wake_lock(int lock, const char* id)

{

    initialize_fds();

//    LOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {

        fd g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];

    }

    else {

        return EINVAL;

    }

    return write(fd, id, strlen(id));

}

到現在爲止,我們的代碼流程已經走了一大半了,我們一開始介紹的android的上面幾層Framework層、JNI層、HAL都已經介紹了就剩下Kernel層了。下面就應該是和kernel層進行交互了。

    但是在android/hardware/libhardware_legacy/power/power.c中的acquire_wake_lock()函數似乎沒法和kernel層進行通信啊??不急 要淡定!!在這個函數的最後不是還有一個返回語句return write(fd, id, strlen(id))!!有人會說這句話看不出什麼啊,我一開始用Source Insight代碼閱讀器跟蹤的時候也沒有找到它的原型,那個叫急啊!!呵呵 最後經過我的繼續不斷的努力查找(其實方法很簡單,既然我從上往下的路斷了,那我就換個方向,我最後又從下往上順着代碼走了一遍),終於被我發現了。

我們先看一下android/kernel/kernel/power/main.c中的一段代碼,我將會做簡單的分析,之後你就會明白剛纔上面所產生的疑問了。

#ifdef CONFIG_USER_WAKELOCK

power_attr(wake_lock);

power_attr(wake_unlock);

#endif

 

static struct attribute g[] {

&state_attr.attr,

#ifdef CONFIG_PM_TRACE

&pm_trace_attr.attr,

#endif

#ifdef CONFIG_PM_SLEEP

&pm_async_attr.attr,

#ifdef CONFIG_PM_DEBUG

&pm_test_attr.attr,

#endif

#ifdef CONFIG_USER_WAKELOCK

&wake_lock_attr.attr,

&wake_unlock_attr.attr,

#endif

#endif

NULL,

};

 

static struct  attribute_group  attr_group {

.attrs g,

};

 

#ifdef CONFIG_PM_RUNTIME

struct workqueue_struct *pm_wq;

EXPORT_SYMBOL_GPL(pm_wq);

 

static int __init pm_start_workqueue(void)

{

pm_wq create_freezeable_workqueue("pm");

 

return pm_wq -ENOMEM;

}

#else

static inline int pm_start_workqueue(void) return 0; }

#endif

 

static int __init pm_init(void)

{

int error pm_start_workqueue();

if (error)

return error;

power_kobj kobject_create_and_add("power", NULL);

if (!power_kobj)

return -ENOMEM;

return sysfs_create_group(power_kobj, &attr_group);

}

core_initcall(pm_init);

這段代碼雖然簡短,但看起來是不是還是比較費勁,沒關係,我們倒過來看就比較清楚了。上面代碼中的sysfs_create_group(power_kobj, &attr_group);的意思就是當我們在對sysfs/下相對的節點進行操作的時候會調用與attr_group裏的相關函數,再往上面看其實就是指&wake_lock_attr.attr(對不同情況的操作會調用不同的attr_group,在第二條路的裏面我們還會再次接觸到這裏)。power_attr(wake_lock)就是使具體的操作函數與其掛鉤。我們現在來看一看這個掛鉤過程是怎麼實現的。

#define power_attr(_name) \

static struct kobj_attribute _name##_attr { \

.attr { \

.name __stringify(_name), \

.mode 0644, \

}, \

.show = _name##_show, \

.store = _name##_store, \

}

在該函數中##的作用通俗點講就是“連接”的意思,比如power_attr(wake_lock)

    static struct kobj_attribute  wake_lock_attr { \

.attr { \

.name __stringify(wake_lock), \

.mode 0644, \

}, \

.show = wake_lock_show, \

.store = wake_lock_store, \

}

 

函數wake_lock_storewake_lock_show就定義在android/kernel/kernel/power/userwakelock.c 

中。因此當我們對/sys/power/wake_lock進行操作的時候就會調用到userwakelock.c中定義的

wake_lock_store()函數。

     好了,我們該回到原來我們產生疑問的地方了,在 power.c中我們將重新研究一下這這段代碼,這時我們還得關注其中的另一個函數initialize_fds()

int  acquire_wake_lock(int lock, const char* id)

{

    initialize_fds();

//    LOGI("acquire_wake_lock lock=%d id='%s'\n", lock, id);

    if (g_error) return g_error;

    int fd;

    if (lock == PARTIAL_WAKE_LOCK) {

        fd g_fds[ACQUIRE_PARTIAL_WAKE_LOCK];

    }

    else {

        return EINVAL;

    }

 return write(fd, id, strlen(id));

}

 

initialize_fds(void)

{

    // XXX: should be this:

    //pthread_once(&g_initialized, open_file_descriptors);

    // XXX: not this:

    if (g_initialized == 0) {

        if(open_file_descriptors(NEW_PATHS) 0) {

            open_file_descriptors(OLD_PATHS);

            on_state "wake";

            off_state "standby";

        }

        g_initialized 1;

    }

}

其實這個函數中最和新的步驟就是open_file_descriptors(NEW_PATHS) ;而

const char const NEW_PATHS[] {

    "/sys/power/wake_lock",

    "/sys/power/wake_unlock",

    "/sys/power/state"

};

    總之經過着一些列的步驟後,最終我們將在 return write(fd, id, strlen(id));時調用android/kernel/kernel/power/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);

        lookup_wake_lock_name(buf, 1, &timeout);

        if (IS_ERR(l)) {

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

        else

                wake_lock(&l->wake_lock);

bad_name:

        mutex_unlock(&tree_lock);

        return n;

}

    該函數執行的基本流程爲:首先調用lookup_wake_lock_name()來獲得指定的喚醒鎖,若延遲參數timeout爲零的話,就調用 wake_lock()否則就調用wake_lock_timeout(),但不管調用哪個最後都會調用到android/kernel/kernel/power/wakelock.c中的函數static void 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_STAT

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

}

#endif

if (!(lock->flags WAKE_LOCK_ACTIVE)) {

lock->flags |= WAKE_LOCK_ACTIVE;

#ifdef CONFIG_WAKELOCK_STAT

lock->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.lu\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_STAT

if (lock == &main_wake_lock)

update_sleep_wait_stats_locked(1);

else if (!wake_lock_active(&main_wake_lock))

update_sleep_wait_stats_locked(0);

#endif

if (has_timeout)

expire_in has_wake_lock_locked(type);

else

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

}

   到這裏爲止,我們走的第一條路就到目的地了,這個函數具體做了什麼,在這裏就不仔細分析了,大家可以自己再跟下或者上網查相關資料,理解這個函數不難。

 

 

   第二部分:系統進入睡眠

有了上面第一部分的學習,再看第二部分的話,會容易很多。假如現在我們按了PAD上的power睡眠鍵,經過一些列的事件處理後,它會調用到PowerManager類中的

  public void goToSleep(long time) 

     

        try {

            mService.goToSleep(time);

        catch (RemoteException e) {

          

    }

而該函數會調用到PowerManagerService中的public void goToSleep()方法;

      public void goToSleep(long time)

      {

        goToSleepWithReason(time, WindowManagerPolicy.OFF_BECAUSE_OF_USER);

   }

     goToSleepWithReason()會調用goToSleepLocked()方法,接着會調用setPowerState();setPowerState()方法裏會調用setScreenStateLocked()setScreenStateLocked()又會調用到Power中的JNI接口setScreenState(),其具體實現是在android_os_Power.cpp文件中; 

      static int setScreenState(JNIEnv *env, jobject clazz, jboolean on) 

     {

          return set_screen_state(on);

     }

 函數中return set_screen_state()的實現是android/hardware/libhardware_legacy/power/power.c

    set_screen_state(int on)

{

    QEMU_FALLBACK(set_screen_state(on));

 

    LOGI("*** set_screen_state %d", on);

 

    initialize_fds();

 

    //LOGI("go_to_sleep eventTime=%lld now=%lld g_error=%s\n", eventTime,

      //      systemTime(), strerror(g_error));

 

    if (g_error) return g_error;

 

    char buf[32];

    int len;

    if(on)

        len snprintf(buf, sizeof(buf), "%s", on_state);

    else

        len snprintf(buf, sizeof(buf), "%s", off_state);

 

    buf[sizeof(buf) 1] '\0';

    len write(g_fds[REQUEST_STATE], buf, len);

    if(len 0) {

        LOGE("Failed setting last user activity: g_error=%d\n", g_error);

    }

    return 0;

    看!!代碼到這裏是不是跟第一部分很相似?不錯,如果接着往下分析的話,可以套用上面第一部分的分析思路,最終len write(g_fds[REQUEST_STATE], buf, len);語句調用的是android//kernel/kernel/power/main.c中的set_screen_state( )

當我們在sys/power/stateandroid/hardware/libhardware_legacy/power/power.c)進行讀寫操作的時候,(linux/kernel/power/main.c)中的state_store()函數會被調用,在該函數中會分成兩個分支:

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;

 

        memchr(buf, '\n', n);

        len buf n;

 

        

        if (len == && !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;

                                   

Android特有的earlysuspend request_suspend_state(state)

Linux標準的suspend:       enter_state(state)

 

注意:如果CONFIG_EARLYSUSPEND宏開的話,kernel會先走earlysuspend,反之則直接走suspend;從這裏開始就要分兩個分支了,如果支持earlysuspend的話就進入 request_suspend_state(state)函數,如果不支持的話就進入標準Linuxenter_state(state)函數。、

這兩個函數分別在兩個文件中kernel/kernel/power/earlysuspend.csuspend.c。現在再回過頭來看的話,感覺整個android中睡眠喚醒機制還是很清晰的。這兩個函數體裏又做了什麼,在這裏就不再做具體分析,大家可以自己對照代碼或者上網查資料,因爲本文的主旨是帶讀者從最上層應用層一直到最底層kernel層,把整個android的睡眠喚醒機制給走通。

 

PowerManager.java                          goToSleep( )

PowerManagerService.java                  goToSleep()

PowerManagerService.java              goToSleepWithReason()

PowerManagerService.java                 setPowerState()

PowerManagerService.java              SetScreenStateLocked ()

Power.java                             setScreenState()

android_os_Power.cpp                    setScreenState()

power.c                                   set_screen_state( )

main.c                                  state_store( )

 

本文屬於原創!!如要轉載,請註明來源處 http://blog.sina.com.cn/s/blog_759dc36b0100stax.html

發佈了37 篇原創文章 · 獲贊 3 · 訪問量 30萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章