android suspend

轉自:http://bbs.ednchina.com/BLOG_ARTICLE_1784575.HTM  並附一些個人修改與註釋。

       在讀這篇轉載博文前,建議先閱讀上一篇的linux suspend文章。

       在一個打過android補丁的內核中, state_store()函數會走另外一條路,會進 入到request_suspend_state()中, 這個文件在earlysuspend.c中. 這些功能都 是android系統加的, 後面會對earlysuspend和late resume 進行介紹.



涉及到的文件:




  • linux_source/kernel/power/main.c
  • linux_source/kernel/power/earlysuspend.c
  • linux_source/kernel/power/wakelock.c


特性介紹



Early Suspend



        Early suspend 是android 引進的一種機制, 這種機制在上游備受爭議,這裏 不做評論.這個機制作用在關閉顯示的時候。在這個時候, 一些和顯示有關的設備, 比如LCD背光, 比如重力感應器, 觸摸屏, 這些設備都會關掉,但是系統可能還是在運行狀態(這時候還有wake lock)進行任務的處理, 例如在掃描 SD卡上的文件等.在嵌入式設備中, 背光是一個很大的電源消耗,所以android會加入這樣一種機制.



Late Resume



       Late Resume 是和suspend 配套的一種機制, 是在內核喚醒完畢開始執行的. 主要就是喚醒在Early Suspend的時候休眠的設備.



Wake Lock



       Wake LockAndroid的電源管理系統中扮演一個核心的角色.Wake Lock是一種鎖的機制,只要有人拿着這個鎖, 系統就無法進入休眠, 可以被用戶態程序和內核獲得.這個鎖可以是有超時的或者是沒有超時的, 超時的鎖會在時間過去以後自動解鎖. 如果沒有鎖了或者超時了, 內核就會啓動休眠的那套機制來進入休眠.



Android Suspend



       當用戶寫入mem 或者 standby到 /sys/power/state中的時候, state_store()會被調用, 然後Android會在這裏調用 request_suspend_state() 而標準的Linux會在這裏進入enter_state()這個函數. 如果請求的是休眠, 那麼early_suspend這個workqueue就會被調用,並且進入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-%02d-%02d %02d:%02d:%02d.%09lu 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



       在early_suspend()函數中, 首先會檢查現在請求的狀態還是否是suspend, 來 防止suspend的請求會在這個時候取消掉(因爲這個時候用戶進程還在運行),如 果需要退出, 就簡單的退出了. 如果沒有, 這個函數就會把early suspend中 註冊的一系列的回調都調用一次, 然後同步文件系統, 然後放棄掉 main_wake_lock, 這個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);
}


Late Resume



       當所有的喚醒已經結束以後, 用戶進程都已經開始運行了, 喚醒通常會是以下的幾種原因:



  • 來電

        如果是來電, 那麼Modem會通過發送命令給rild來讓rild通知WindowManager有 來電響應,這樣就會遠程調用PowerManagerService來寫"on" 到 /sys/power/state 來執行late resume的設備, 比如點亮屏幕等.



  • 用戶按鍵用戶按鍵事件會送到WindowManager中, WindowManager會處理這些 按鍵事件,按鍵分爲幾種情況, 如果案件不是喚醒鍵(能夠喚醒系統的按鍵) 那麼WindowManager會主動放棄wakeLock來使系統進入再次休眠, 如果按鍵 是喚醒鍵,那麼WindowManger就會調用PowerManagerService中的接口來執行 Late Resume.
  • Late Resume 會依次喚醒前面調用了Early Suspend的設備.
static void late_resume(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 == SUSPENDED)
state &= ~SUSPENDED;
else
abort = 1;
spin_unlock_irqrestore(&state_lock, irqflags);

if (abort) {
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: abort, state %d\n", state);
goto abort;
}
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: call handlers\n");
list_for_each_entry_reverse(pos, &early_suspend_handlers, link)
if (pos->resume != NULL)
pos->resume(pos);
if (debug_mask & DEBUG_SUSPEND)
pr_info("late_resume: done\n");
abort:
mutex_unlock(&early_suspend_lock);
}


Wake Lock



        我們接下來看一看wake lock的機制是怎麼運行和起作用的, 主要關注 wakelock.c文件就可以了.


         wake lock 有加鎖和解鎖兩種狀態, 加鎖的方式有兩種, 一種是永久的鎖住, 這樣的鎖除非顯示的放開, 是不會解鎖的, 所以這種鎖的使用是非常小心的. 第二種是超時鎖, 這種鎖會鎖定系統喚醒一段時間, 如果這個時間過去了, 這個鎖會自動解除.


鎖有兩種類型:



  1. WAKE_LOCK_SUSPEND 這種鎖會防止系統進入睡眠
  2. WAKE_LOCK_IDLE 這種鎖不會影響系統的休眠, 作用我不是很清楚.

這裏補充一下,在linux kernel中include/linux/wakelock.h中有如下注釋:

/* A wake_lock prevents the system from entering suspend or other low power
 * states when active.

 *If the type is set to WAKE_LOCK_SUSPEND, the wake_lock prevents a full        *system suspend.

* If the type is WAKE_LOCK_IDLE, low power states that cause large interrupt *latencies or that disable a set of interrupts will not entered from idle until the *wake_locks are released.
 */

翻譯如下:

       當喚醒鎖被激活的時候,它能阻止系統進入掛起狀態或者其他低功耗模式。

       如果鎖類型被設置爲 WAKE_LOCK_SUSPEND,該喚醒鎖阻止整個系統進入休眠狀態。

       如果鎖類型被設置爲 WAKE_LOCK_IDLE,  除非該鎖被釋放否則產生大量中斷潛在因素或者禁止一系列中斷的系統低功耗模式被阻止進入。但是系統可以進入掛起睡眠狀態。

 


在wake lock中, 會有3個地方讓系統直接開始suspend(), 分別是:



  1. 在wake_unlock()中, 如果發現解鎖以後沒有任何其他的wake lock了, 就開始休眠
  2. 在定時器都到時間以後, 定時器的回調函數會查看是否有其他的wake lock, 如果沒有, 就在這裏讓系統進入睡眠.
  3. 在wake_lock() 中, 對一個wake lock加鎖以後, 會再次檢查一下有沒有鎖, 我想這裏的檢查是沒有必要的, 更好的方法是使加鎖的這個操作原子化, 而不是繁冗的檢查. 而且這樣的檢查也有可能漏掉.


Suspend



當wake_lock 運行 suspend()以後, 在wakelock.c的suspend()函數會被調用,這個函數首先sync文件系統,然後調用pm_suspend(request_suspend_state),接下來pm_suspend()就會調用enter_state()來進入Linux的休眠流程..

      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 (current_event_num == entry_event_num) {
wake_lock_timeout(&unknown_wakeup, HZ / 2);
}
}


Android休眠與標準Linux休眠的區別



pm_suspend() 雖然會調用enter_state()來進入標準的Linux休眠流程,但是還是有一些區別:



  • 當進入凍結進程的時候, android首先會檢查有沒有wake lock,如果沒有, 纔會停止這些進程, 因爲在開始suspend和凍結進程期間有可能有人申請了 wake lock,如果是這樣, 凍結進程會被中斷.
  • 在suspend_late()中, 會最後檢查一次有沒有wake lock, 這有可能是某種快速申請wake lock,並且快速釋放這個鎖的進程導致的,如果有這種情況, 這裏會返回錯誤, 整個suspend就會全部放棄.如果pm_suspend()成功了,LOG的輸出可以通過在kernel cmd裏面增加 "no_console_suspend" 來看到suspend和resume過程中的log輸出。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章