天天看點

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

轉自:http://blog.sina.com.cn/s/blog_759dc36b0100stax.html

但标準的Linux睡眠喚醒機制有其自身的一些缺陷(所有子產品必須同時睡下或者喚醒),在某些情況下,這會導緻能耗的白白浪費。是以Android在标準Linux睡眠喚醒的機制上作了新的改動(wake_lock喚醒、early_suspend和late_resume機制),進而很好的解決上面的問題。本文将以Android2.3.1版本為例,詳細介紹标準Linux睡眠/喚醒是如何工作的, 并且Android中是如何把其自身特有的機制和Linux中标準的聯系起來的。

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

    在标準Linux中,休眠主要分三個主要的步驟:(1)當機使用者态程序和核心态任務;(2)調用注冊的裝置的suspend的回調函數,其調用順序是按照驅動加載時的注冊順序。(3)休眠核心裝置和使CPU進入休眠态當機程序是核心把程序清單中所有的程序的狀态都設定為停止,并且儲存下所有程序的上下文。 當這些程序被解凍的時候,它們是不知道自己被當機過的,隻是簡單的繼續執行。

    那麼是如何讓Linux進入休眠的呢?其實很簡單,因為Android和kernel已經做了很多複雜的工作,是以使用者隻需可以通過讀寫sys檔案/sys /power/state 就可以實作控制系統進入休眠。 

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

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

當然還有其它的狀态操作,在下面的内容中将有介紹。

  Android睡眠喚醒機制簡介:

Android在Linux核心原有的睡眠喚醒子產品上基礎上,主要增加了下面三個機制:

    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/linux(earlysuspend、lateresume)睡眠喚醒機制簡介

    接下來我們将以上圖的架構結構為主線,将進行非常非常詳細地從最上層到最底層的跟蹤!!!本文的主旨主要就是讀者從Android最上層(Java寫的應用程式)一步一步的往下跟進,經過Java、C++和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類接口時所傳遞的參數進行初步的分析和對應的設定,并管理一個喚醒鎖隊列,然後配合其他子產品(例如WatchDog、BatteryService、ShutdownThread 等)的狀态資訊,做出決策,調用Power類的對應接口,最終通過JNI 接口,調用到硬體抽象層中的函數,對sysfs 的使用者接口進行操作,進而觸發核心态實作的用。

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

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

    Power.java:被PowerManagerService類調用;

    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 ? 0 : -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_store和wake_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);

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

        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/state(android/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;

        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;

}                                    

Android特有的earlysuspend: request_suspend_state(state)

Linux标準的suspend:       enter_state(state)

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

這兩個函數分别在兩個檔案中kernel/kernel/power/earlysuspend.c和suspend.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( )

繼續閱讀