版本信息
Linux Kernel: v2.6.28
Android: v2.0
對於休眠(suspend)的簡單介紹
在Linux中,休眠主要分三個主要的步驟: linux
凍結用戶態進程和內核態任務
調用註冊的設備的suspend的回調函數
順序是按照註冊順序
休眠核心設備和使CPU進入休眠態凍結進程是內核把進程列表中全部的進程的狀態都設置爲中止,而且保存下全部進程的上下文. 當這些進程被解凍的時候,他們是不知道本身被凍結過的,只是簡單的繼續執行.如何讓Linux進入休眠呢?用戶能夠經過讀寫sys文件/sys /power/state 是實現控制系統進入休眠. 好比
# echo standby > /sys/power/state命令系統進入休眠. 也可使用 app
# cat /sys/power/state來獲得內核支持哪幾種休眠方式. 函數
Linux Suspend 的流程
相關的文件:
你能夠經過訪問 Linux內核網站 來獲得源代碼,下面是文件的路徑: 網站
linux_soruce/kernel/power/main.c
linux_source/kernel/arch/xxx/mach-xxx/pm.c
linux_source/driver/base/power/main.c
接下來讓咱們詳細的看一下Linux是怎麼休眠/喚醒的. Let 's going to see how these happens. debug
用戶對於/sys/power/state 的讀寫會調用到 main.c中的state_store(), 用戶能夠寫入 const char * const pm_state[] 中定義的字符串, 好比"mem", "standby". 設計
而後state_store()會調用enter_state(), 它首先會檢查一些狀態參數,而後同步文件系統. 下面是代碼: 調試
static int enter_state(suspend_state_t state){ rest
int error; orm
if (!valid_state(state)) 進程
return -ENODEV;
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
printk(KERN_INFO "PM: Syncing filesystems ... ");
sys_sync();
printk("done.");
pr_debug("PM: Preparing system for %s sleep ", pm_states[state]);
error = suspend_prepare();
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
pr_debug("PM: Entering %s sleep", pm_states[state]);
error = suspend_devices_and_enter(state);
Finish: pr_debug("PM: Finishing wakeup.");
suspend_finish();
Unlock: mutex_unlock(&pm_mutex);
return error;}
準備, 凍結進程
當進入到suspend_prepare()中之後, 它會給suspend分配一個虛擬終端來輸出信 息, 而後廣播一個系統要進入suspend的Notify, 關閉掉用戶態的helper進程, 而後一次調用suspend_freeze_processes()凍結全部的進程, 這裏會保存全部進程 當前的狀態, 也許有一些進程會拒絕進入凍結狀態, 當有這樣的進程存在的時候, 會致使凍結失敗,此函數就會放棄凍結進程,而且解凍剛纔凍結的全部進程.
static int suspend_prepare(void){
int error;
unsigned int free_pages;
if (!suspend_ops || !suspend_ops->enter)
return -EPERM;
pm_prepare_console();
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
error = usermodehelper_disable();
if (error)
goto Finish;
if (suspend_freeze_processes()) {
error = -EAGAIN;
goto Thaw;
}
free_pages = global_page_state(NR_FREE_PAGES);
if (free_pages < FREE_PAGE_NUMBER) {
pr_debug("PM: free some memory");
shrink_all_memory(FREE_PAGE_NUMBER - free_pages);
if (nr_free_pages() < FREE_PAGE_NUMBER) {
error = -ENOMEM;
printk(KERN_ERR "PM: No enough memory");
}
}
if (!error)
return 0;
Thaw:
suspend_thaw_processes();
usermodehelper_enable();
Finish:
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
讓外設進入休眠
如今, 全部的進程(也包括workqueue/kthread) 都已經中止了, 內核態人物有 可能在中止的時候握有一些信號量, 因此若是這時候在外設裏面去解鎖這個信號 量有可能會發生死鎖, 因此在外設的suspend()函數裏面做lock/unlock鎖要很是當心,這裏建議設計的時候就不要在suspend()裏面等待鎖. 並且由於suspend的時候,有一些Log是沒法輸出的,因此一旦出現問題,很是難調試.
而後kernel在這裏會嘗試釋放一些內存.
最後會調用suspend_devices_and_enter()來把全部的外設休眠, 在這個函數中, 若是平臺註冊了suspend_pos(一般是在板級定義中定義和註冊), 這裏就會調用 suspend_ops->begin(), 而後driver/base/power/main.c 中的 device_suspend()->dpm_suspend() 會被調用,他們會依次調用驅動的suspend() 回調來休眠掉全部的設備.
當全部的設備休眠之後, suspend_ops->prepare()會被調用, 這個函數一般會做 一些準備工做來讓板機進入休眠. 接下來Linux,在多核的CPU中的非啓動CPU會被關掉, 經過註釋看到是避免這些其餘的CPU形成race condion,接下來的之後只有一個CPU在運行了.
suspend_ops 是板級的電源管理操做, 一般註冊在文件 arch/xxx/mach-xxx/pm.c 中.
接下來, suspend_enter()會被調用, 這個函數會關閉arch irq, 調用 device_power_down(), 它會調用suspend_late()函數, 這個函數是系統真正進入 休眠最後調用的函數, 一般會在這個函數中做最後的檢查. 若是檢查沒問題, 接 下來休眠全部的系統設備和總線, 而且調用 suspend_pos->enter() 來使CPU進入 省電狀態. 這時候,就已經休眠了.代碼的執行也就停在這裏了.
int suspend_devices_and_enter(suspend_state_t state){
int error, ftrace_save;
if (!suspend_ops)
return -ENOSYS;
if (suspend_ops->begin) {
error = suspend_ops->begin(state);
if (error)
goto Close;
}
suspend_console();
ftrace_save = __ftrace_enabled_save();
suspend_test_start();
error = device_suspend(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
if (suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
goto Resume_devices;
}
if (suspend_test(TEST_PLATFORM))
goto Finish;
error = disable_nonboot_cpus();
if (!error && !suspend_test(TEST_CPUS))
suspend_enter(state);
enable_nonboot_cpus();
Finish:
if (suspend_ops->finish)
suspend_ops->finish();
Resume_devices:
suspend_test_start();
device_resume(PMSG_RESUME);
suspend_test_finish("resume devices");
__ftrace_enabled_restore(ftrace_save);
resume_console();
Close:
if (suspend_ops->end)
suspend_ops->end();
return error;
Recover_platform:
if (suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
Resume
若是在休眠中系統被中斷或者其餘事件喚醒, 接下來的代碼就會開始執行, 這個喚醒的順序是和休眠的循序相反的,因此係統設備和總線會首先喚醒,使能系統中 斷, 使能休眠時候中止掉的非啓動CPU, 以及調用suspend_ops->finish(), 並且在suspend_devices_and_enter()函數中也會繼續喚醒每一個設備,使能虛擬終端, 最後調用 suspend_ops->end().
在返回到enter_state()函數中的, 當 suspend_devices_and_enter() 返回之後, 外設已經喚醒了, 可是進程和任務都仍是凍結狀態, 這裏會調用suspend_finish()來解凍這些進程和任務, 並且發出Notify來表示系統已經從suspend狀態退出, 喚醒終端.
到這裏, 全部的休眠和喚醒就已經完畢了, 系統繼續運行了.