在Linux核心中,suspend/resume過程是一種非常重要的過程。它可以實現系統的休眠和喚醒,提高系統的能源效率和反應速度。在本文中,我們將深入探討Linux Kernel suspend/resume 流程的實作原理與相關技術。
#休眠/喚醒在嵌入式Linux中是非常重要的部分,嵌入式設備盡可能的進入休眠狀態來延長電池的續航時間.這篇文章就詳細介紹一下Linux中休眠/喚醒是如何工作的
參考文章:作者: zhangjiejing
#我的linux核心版本:3.0.31
對於休眠(suspend)的簡單介紹
在Linux中,休眠主要分三個主要的步驟:
1、凍結用戶態進程和核心態任務
2、呼叫註冊的裝置的suspend的回呼函數
3、順序是依照註冊順序
休眠核心設備和使CPU進入休眠態凍結進程是核心把進程列表中所有的進程的狀態都設定為停止,並且保存下所有進程的上下文. 當這些進程被解凍的時候,他們是不知道自己被凍結過的,只是簡單的繼續執行.如何讓Linux進入休眠呢?用戶可以透過讀寫sys檔/sys /power/state 是實現控制系統進入休眠. 例如
# echo mem > /sys/power/state
指令系統進入休眠. 也可以使用
# cat /sys/power/state
來得到核心支援哪幾種休眠方式.
Linux Suspend 的流程
相關的檔案:
你可以透過造訪Linux核心網站來得到原始碼,下面是檔案的路徑:
kernel/kernel/power/main.c kernel/kernel/power/suspend.c kernel/driver/base/power/main.c
接下來讓我們詳細的看一下Linux是怎麼休眠/喚醒的. Let ‘s going to see how these happens.
使用者對於/sys/power/state 的讀寫會呼叫到main.c中的state_store(), 使用者可以寫入const char * const pm_state[] 中定義的字串, 例如”mem”, “standby ”.當然一般是由suspend和resume的按鍵控制的
然後state_store()會呼叫enter_state(), 它會先檢查一些狀態參數,然後同步檔案系統. 下面是程式碼:
1. /** 2. \* enter_state - Do common work of entering low-power state. 3. \* @state: pm_state structure for state we're entering. 4. \* 5. \* Make sure we're the only ones trying to enter a sleep state. Fail 6. \* if someone has beat us to it, since we don't want anything weird to 7. \* happen when we wake up. 8. \* Then, do the setup for suspend, enter the state, and cleaup (after 9. \* we've woken up). 10. */ 11. int enter_state(suspend_state_t state) 12. { 13. int error; 14. 15. if (!valid_state(state)) 16. return -ENODEV; 17. 18. if (!mutex_trylock(&pm_mutex)) 19. return -EBUSY; 20. 21. printk(KERN_INFO "PM: Syncing filesystems ... "); 22. sys_sync(); 23. printk("done.\n"); 24. 25. pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]); 26. error = suspend_prepare(); 27. if (error) 28. goto Unlock; 29. 30. if (suspend_test(TEST_FREEZER)) 31. goto Finish; 32. 33. pr_debug("PM: Entering %s sleep\n", pm_states[state]); 34. pm_restrict_gfp_mask(); 35. error = suspend_devices_and_enter(state); 36. pm_restore_gfp_mask(); 37. 38. Finish: 39. pr_debug("PM: Finishing wakeup.\n"); 40. suspend_finish(); 41. Unlock: 42. mutex_unlock(&pm_mutex); 43. return error; 44. }
準備, 凍結進程
當進入到suspend_prepare()中以後, 它會給suspend分配一個虛擬終端來輸出訊息, 然後廣播一個系統要進入suspend的Notify, 關閉掉用戶態的helper進程, 然後依次調用suspend_freeze_processes()凍結所有的進程, 這裡會保存所有進程當前的狀態, 也許有一些進程會拒絕進入凍結狀態, 當有這樣的進程存在的時候, 會導致凍結失敗,此函數就會放棄凍結進程,並且解凍剛才凍結的所有進程.
1. /** 2. \* suspend_prepare - Do prep work before entering low-power state. 3. \* 4. \* This is common code that is called for each state that we're entering. 5. \* Run suspend notifiers, allocate a console and stop all processes. 6. */ 7. static int suspend_prepare(void) 8. { 9. int error; 10. 11. if (!suspend_ops || !suspend_ops-**>**enter) 12. return -EPERM; 13. 14. pm_prepare_console(); 15. 16. error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); 17. if (error) 18. goto Finish; 19. 20. error = usermodehelper_disable(); 21. if (error) 22. goto Finish; 23. 24. error = suspend_freeze_processes(); 25. if (!error) 26. return 0; 27. 28. suspend_thaw_processes(); 29. usermodehelper_enable(); 30. Finish: 31. pm_notifier_call_chain(PM_POST_SUSPEND); 32. pm_restore_console(); 33. return error; 34. }
讓外設進入休眠
現在, 所有的進程(也包括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進入省電狀態. 這時候,就已經休眠了.代碼的執行也就停在這裡了
1. /** 2. \* suspend_devices_and_enter - suspend devices and enter the desired system 3. \* sleep state. 4. \* @state: state to enter 5. */ 6. int suspend_devices_and_enter(suspend_state_t state) 7. { 8. int error; 9. 10. if (!suspend_ops) 11. return -ENOSYS; 12. 13. trace_machine_suspend(state); 14. if (suspend_ops-**>**begin) { 15. error = suspend_ops-**>**begin(state); 16. if (error) 17. goto Close; 18. } 19. suspend_console(); 20. suspend_test_start(); 21. error = dpm_suspend_start(PMSG_SUSPEND); 22. if (error) { 23. printk(KERN_ERR "PM: Some devices failed to suspend\n"); 24. goto Recover_platform; 25. } 26. suspend_test_finish("suspend devices"); 27. if (suspend_test(TEST_DEVICES)) 28. goto Recover_platform; 29. 30. error = suspend_enter(state); 31. 32. Resume_devices: 33. suspend_test_start(); 34. dpm_resume_end(PMSG_RESUME); 35. suspend_test_finish("resume devices"); 36. resume_console(); 37. Close: 38. if (suspend_ops-**>**end) 39. suspend_ops-**>**end(); 40. trace_machine_suspend(PWR_EVENT_EXIT); 41. return error; 42. 43. Recover_platform: 44. if (suspend_ops-**>**recover) 45. suspend_ops-**>**recover(); 46. goto Resume_devices; 47. } RESUME
如果在休眠中系统被中断或者其他事件唤醒, 接下来的代码就会开始执行, 这个 唤醒的顺序是和休眠的循序相反的,所以系统设备和总线会首先唤醒,使能系统中 断, 使能休眠时候停止掉的非启动CPU, 以及调用suspend_ops->finish(), 而且 在suspend_devices_and_enter()函数中也会继续唤醒每个设备,使能虚拟终端, 最后调用 suspend_ops->end().
在返回到enter_state()函数中的, 当 suspend_devices_and_enter() 返回以后, 外设已经唤醒了, 但是进程和任务都还是冻结状态, 这里会调用suspend_finish()来解冻这些进程和任务, 而且发出Notify来表示系统已经从suspend状态退出, 唤醒终端.
到这里, 所有的休眠和唤醒就已经完毕了, 系统继续运行了.
总之,suspend/resume过程是Linux内核中不可或缺的一部分。它可以实现系统的休眠和唤醒,提高系统的能效和响应速度。希望本文能够帮助读者更好地理解Linux Kernel suspend/resume 过程的实现原理和相关技术。
以上是深入探討Linux Kernel suspend/resume 流程的實現原理與相關技術的詳細內容。更多資訊請關注PHP中文網其他相關文章!