Linux电源管理(5)_Hibernate和Sleep功能介绍【转】
本文轉(zhuǎn)載自:http://www.wowotech.net/pm_subsystem/std_str_func.html
1. 前言
Hibernate和Sleep兩個功能是Linux Generic PM的核心功能,它們的目的是類似的:暫停使用——>保存上下文——>關(guān)閉系統(tǒng)以節(jié)電········>恢復(fù)系統(tǒng)——>恢復(fù)上下文——>繼續(xù)使用。
本文以內(nèi)核向用戶空間提供的接口為突破口,從整體上對這兩個功能進行介紹,并會在后續(xù)的文章中,分析它們的實現(xiàn)邏輯和執(zhí)行動作。
順便感概一下,雖然這些機制在Linux系統(tǒng)中存在很久了(類似的概念也存在于Windows系統(tǒng)中),但以蝸蝸的觀察,它們被使用的頻率并不是很高,特別是在PC上,大多數(shù)人在大多數(shù)時候選擇直接關(guān)閉系統(tǒng)。陰錯陽差的是,在很多嵌入式設(shè)備中,設(shè)計者會利用Sleep機制實現(xiàn)熱關(guān)機功能,以此減少開機的時間。
2. Hibernate和Sleep相關(guān)的術(shù)語梳理
蝸蝸在“Generic PM之基本概念和軟件架構(gòu)”中提到了Linux Generic PM有關(guān)的多個詞匯,如Hibernate、Sleep、Suspend、Standby等等,聽起來有些亂,因此在介紹Hibernate和Sleep之前,先來理一下這些詞匯的關(guān)系。
▆ Hibernate(冬眠)和Sleep(睡眠) 是Linux電源管理在用戶角度的抽象,是用戶可以看到的實實在在的東西。它們的共同點,是保存系統(tǒng)運行的上下文后掛起(suspend)系統(tǒng),并在系統(tǒng)恢復(fù)后接著運行,就像什么事情都沒有發(fā)生一樣。它們的不同點,是上下文保存的位置、系統(tǒng)恢復(fù)的觸發(fā)方式以及具體的實現(xiàn)機制。 ▆ Suspend 有兩個層次的含義。一是Hibernate和Sleep功能在底層實現(xiàn)上的統(tǒng)稱,都是指掛起(Suspend)系統(tǒng),根據(jù)上下文的保存位置,可以分為Suspend to Disk(STD,即Hibernate,上下文保存在硬盤/磁盤中)和Suspend to RAM(STR,為Sleep的一種,上下文保存在RAM中);
二是Sleep功能在代碼級的實現(xiàn),表現(xiàn)為“kernel/power/suspend.c”文件。 ▆ Standby,是Sleep功能的一個特例,可以翻譯為“打盹”。 正常的Sleep(STR),會在處理完上下文后,由arch-dependent代碼將CPU置為低功耗狀態(tài)(通常為Sleep)。而現(xiàn)實中,根據(jù)對功耗和睡眠喚醒時間的不同需求,CPU可能會提供多種低功耗狀態(tài),如除Sleep之外,會提供Standby狀態(tài),該狀態(tài)下,CPU處于淺睡眠模式,有任何的風(fēng)吹草動,
就會立即醒來。 ▆ Wakeup 這是我們第一次正式的提出Wakeup的概念。我們多次提到恢復(fù)系統(tǒng),其實在內(nèi)核中稱為Wakeup。表面上,wakeup很簡單,無論是冬眠、睡眠還是打盹,總得有一個刺激讓我們回到正常狀態(tài)。但復(fù)雜的就是,什么樣的刺激才能讓我們醒來? 動物界,溫度回升可能是唯一可以讓動物從冬眠狀態(tài)醒來的刺激。而踢一腳、鬧鐘響等刺激,則可以讓我們從睡眠狀態(tài)喚醒。對于打盹來說,則任何的風(fēng)吹草動,都可以喚醒。 而在計算機界,冬眠(Hibernate)時,會關(guān)閉整個系統(tǒng)的供電,因此想醒來,唯有Power按鈕可用。而睡眠時,為了縮短Wakeup時間,并不會關(guān)閉所有的供電,另外,為了較好的用戶體驗,通常會保留某些重要設(shè)備的供電(如鍵盤),那樣這些設(shè)備就可以喚醒系統(tǒng)。 這些刻意保留下來的、可以喚醒系統(tǒng)的設(shè)備,統(tǒng)稱為喚醒源(Wakeup source)。而Wakeup source的選擇,則是PM設(shè)計工作(特別是Sleep、Standby等功能)的重點。
經(jīng)過上面的解釋后,為了統(tǒng)一,蝸蝸會把表述從用戶角度(Hibernate和Sleep)切換為底層實現(xiàn)上(STD、STR和Standby)。
3. 軟件架構(gòu)及模塊匯整
3.1 軟件架構(gòu)
內(nèi)核中該部分的軟件架構(gòu)大概可以分為三個層次,如下圖:
1)API Layer,描述用戶空間API的一個抽象層。
這里的API有兩類,一類涉及Hibernate和Sleep兩個功能(global APIs),包括實際功能、測試用功能、Debug用功能等,通過sysfs和debugfs兩種形式提供;另一類是Hibernate特有的(STD APIs),通過sysfs和字符設(shè)備兩種形式提供。
2)PM Core,電源管理的核心邏輯層,位于kernel/power/目錄下,包括主功能(main)、STD、STR&Standby以及輔助功能(assistant)等多個子模塊。
主功能,主要負責(zé)實現(xiàn)global APIs相關(guān)的邏輯,為用戶空間提供相應(yīng)的API;
STD,包括hibernate、snapshot、swap、block_io等子模塊,負責(zé)實現(xiàn)STD功能和硬件無關(guān)的邏輯;
STR&Stanby,包括suspend和suspend_test兩個子模塊,負責(zé)實現(xiàn)STR、Standby等功能和硬件無關(guān)的邏輯。
3)PM Driver,電源管理驅(qū)動層,涉及體系結(jié)構(gòu)無關(guān)驅(qū)動、體系結(jié)構(gòu)有關(guān)驅(qū)動、設(shè)備模型以及各個設(shè)備驅(qū)動等多個軟件模塊。
3.2 用戶空間接口
3.2.1 /sys/power/state
state是sysfs中一個文件,為Generic PM的核心接口,在“kernel/power/main.c”中實現(xiàn),用于將系統(tǒng)置于指定的Power State(供電模式,如Hibernate、Sleep、Standby等)。不同的電源管理功能,在底層的實現(xiàn),就是在不同Power State之間切換。
讀取該文件,返回當前系統(tǒng)支持的Power State,形式為字符串。在內(nèi)核中,有兩種類型的Power State,一種是Hibernate相關(guān)的,名稱為“disk”,除“disk”之外,內(nèi)核在"kernel/power/suspend.c"中通過數(shù)組的形式定義了另外3個state,如下:
1: const char *const pm_states[PM_SUSPEND_MAX] = {2: [PM_SUSPEND_FREEZE] = "freeze",3: [PM_SUSPEND_STANDBY] = "standby",4: [PM_SUSPEND_MEM] = "mem",5: };這些Power State的解釋如下:
▆ freeze
這種Power State,并不涉及具體的Hardware或Driver,只是凍結(jié)所有的進程,包括用戶空間進程及內(nèi)核線程。和我們熟知的“冬眠”和“睡眠”相比,就稱為“閉目養(yǎng)神”吧(可想而知,能節(jié)省的能量是有限的)。
【注:我們在之前的描述中,并沒有特別描述該State,因為它在較早的內(nèi)核中,只是Sleep、Hibernate等功能的一部分,只是在近期才獨立出來。另外一個原因是,該state的省電效果不是很理想,所以其引用場景也是有限的。】
▆ standby,即第2章所描述的Standby狀態(tài)。
▆ mem,即通常所講的Sleep功能,也是第2章所描述的STR,Suspend to RAM。
▆ disk,即Hibernate功能,也是第2章所描述的STD,Suspend to Disk。
寫入特定的Power State字符串,將會把系統(tǒng)置為該模式。
3.2.2 /sys/power/pm_trace
PM Trace用于提供電源管理過程中的Trace記錄,由“CONFIG_PM_TRACE”宏定義(kernel/power/Kconfig)控制是否編譯進內(nèi)核,并由“/sys/power/pm_trace”文件在運行時控制是否使能該功能。
該功能的具體實現(xiàn)是“平臺相關(guān)”的,我們這里暫不描述。
3.2.3 /sys/power/pm_test
PM test用于對電源管理功能的測試,由“CONFIG_PM_DEBUG”宏定義(kernel/power/Kconfig)控制是否編譯進內(nèi)核。其核心思想是:
▆ 將電源管理過程按照先后順序,劃分為多個步驟,如core、platform、devices等。這些步驟稱作PM Test Level。 ▆ 系統(tǒng)通過一個全局變量(pm_test_level),保存系統(tǒng)當前的PM Test Level。該變量的值可以通過”/sys/power/pm_test“文件獲取及修改。 ▆ 在每一個電源管理步驟結(jié)束后,插入PM test代碼,該代碼以當前執(zhí)行步驟為參數(shù),會判斷當前的PM Test Level和執(zhí)行步驟是否一致,如果一致,則說明該步驟執(zhí)行成功。出于Test考量,執(zhí)行成功后,系統(tǒng)會打印Test信息,并在等待一段時間后,退出PM過程。 ▆ 開發(fā)人員可以通過修改全局的Test Level,有目的測試所關(guān)心的步驟是否執(zhí)行成功。上面已經(jīng)講了,該文件用于獲取及修改PM Test Level,具體的Level信息在“kernel/power/main.c”中定義,格式如下(具體的意義,比較簡單,對著相關(guān)的代碼看,非常清晰,這里就不啰嗦了):
1: static const char * const pm_tests[__TEST_AFTER_LAST] = {2: [TEST_NONE] = "none",3: [TEST_CORE] = "core",4: [TEST_CPUS] = "processors",5: [TEST_PLATFORM] = "platform",6: [TEST_DEVICES] = "devices",7: [TEST_FREEZER] = "freezer",8: };3.2.4 /sys/power/wakeup_count
該接口只和Sleep功能有關(guān),因此由“CONFIG_PM_SLEEP”宏定義(kernel/power/Kconfig)控制。它的存在,是為了解決Sleep和Wakeup之間的同步問題。
我們知道,系統(tǒng)睡眠后,可以通過保留的Wakeup source喚醒系統(tǒng)。而在當今的CPU體系中,喚醒系統(tǒng)就是喚醒CPU,而喚醒CPU的唯一途徑,就是Wakeup source產(chǎn)生中斷(內(nèi)核稱作Wakeup event)。而內(nèi)核要保證在多種狀態(tài)下,Sleep/Wakeup的行為都能正常,如下:
▆ 系統(tǒng)處于sleep狀態(tài)時,產(chǎn)生了Wakeup event。此時應(yīng)該直接喚醒系統(tǒng)。這一點沒有問題。 ▆ 系統(tǒng)在進入sleep的過程中,產(chǎn)生了Wakeup event。此時應(yīng)該放棄進入sleep。 這一點就不那么容易做到了。例如,當Wakeup event發(fā)生在“/sys/power/state”被寫之后、內(nèi)核執(zhí)行freeze操作之前。此時用戶空間程序依舊可以處理Wakeup event,或者只是部分處理。而內(nèi)核卻以為該Event已經(jīng)被處理,因此并不會放棄此次sleep動作。 這就會造成,Wakeup event發(fā)生后,用戶空間程序已經(jīng)后悔了,不想睡了,但最終還是睡下去了。直到下一個Wakeup event到來。為了解決上面的問題,內(nèi)核提供wkaeup_count機制,配合“/sys/power/state”,以實現(xiàn)Sleep過程中的同步。該機制的操作行為如下:
▆ wakeup_count是內(nèi)核用來保存當前wakeup event發(fā)生的計數(shù)。 ▆ 用戶空間程序在寫入state切換狀態(tài)之前,應(yīng)先讀取wakeup_count并把獲得的count寫回給wakeup_count。 ▆ 內(nèi)核會比對寫回的count和當前的count是否一致,如果不一致,說明在讀取/寫回操作之間,產(chǎn)生了新的的wakeup event,內(nèi)核就會返回錯誤。 ▆ 用戶空間程序檢測到寫入錯誤之后,不能繼續(xù)后的動作,需要處理響應(yīng)的event并伺機再次讀取/寫回wakeup_count。 ▆ 如果內(nèi)核比對一致,會記錄write wakeup_count成功時的event快照,后面繼續(xù)suspend動作時,會檢查是否和快照相符,如果不符,會終止suspend。 ▆ 用戶空間程序檢測到寫入正確后,可以繼續(xù)對state的寫入,以便發(fā)起一次狀態(tài)切換。而此時是安全的。蝸蝸會在后續(xù)的文章中,詳細描述該機制在內(nèi)核中的實現(xiàn)邏輯,這里暫不做進一步說明。
3.2.5 /sys/power/disk
該接口是STD特有的。用于設(shè)置或獲取STD的類型。當前內(nèi)核支持的STD類型包括:
1: static const char * const hibernation_modes[] = {2: [HIBERNATION_PLATFORM] = "platform",3: [HIBERNATION_SHUTDOWN] = "shutdown",4: [HIBERNATION_REBOOT] = "reboot",5: #ifdef CONFIG_SUSPEND6: [HIBERNATION_SUSPEND] = "suspend",7: #endif8: };▆ platform,表示使用平臺特有的機制,處理STD操作,如使用hibernation_ops等。
▆ shutdown,通過關(guān)閉系統(tǒng)實現(xiàn)STD,內(nèi)核會調(diào)用kernel_power_off接口。
▆ reboot,通過重啟系統(tǒng)實現(xiàn)STD,內(nèi)核會調(diào)用kernel_restart接口。
【注:以上兩個kernel_xxx接口的實現(xiàn),可參考“Generic PM之Reboot過程”。】
▆ suspend,利用STR功能,實現(xiàn)STD。該類型下,STD和STR底層的處理邏輯類似。
3.2.6 /sys/power/image_size
該接口也是STD特有的。我們知道,STD的原理是將當前的運行上下文保存在系統(tǒng)的disk(如NAND Flash,如硬盤),然后選擇合適的方式關(guān)閉或重啟系統(tǒng)。保存上下文是需要存儲空間的,不光是disk中的存儲空間,也包括位于內(nèi)存的用于交換或緩沖的空間。
而該接口,就是設(shè)置或者獲取當前內(nèi)存中需要分配多少空間,用于緩沖需要寫入到disk的數(shù)據(jù)。單位為byte。
3.2.6 /sys/power/reserverd_size
reserverd_size用于指示預(yù)留多少內(nèi)存空間,用于在->freeze() 和 ->freeze_noirq()過程中保存設(shè)備驅(qū)動分配的空間。以免在STD的過程中丟失。
3.2.7 /sys/power/resume
該接口也是STD特有的。正常情況下,在重新開機后,內(nèi)核會在后期的初始化過程中,讀取保存在disk中的image,并恢復(fù)系統(tǒng)。而該接口,提供了一種在用戶空間手動的讀取image并恢復(fù)系統(tǒng)的方法。
通常情況下,該操作出現(xiàn)在系統(tǒng)正常運行的過程中,需要加載并執(zhí)行另外的image。
3.2.8 debugfs/suspend_status
該接口是以debugfs的形式,向用戶空間提供suspend過程的統(tǒng)計信息,包括:成功的次數(shù)、失敗的次數(shù)、freeze失敗的次數(shù)等等。
3.2.9 /dev/snapshot
該接口也是STD特有的。它通過字符設(shè)備的形式,向用戶空間提供software的STD操作。我們會在后續(xù)的文章中詳細描述。
總結(jié)
以上是生活随笔為你收集整理的Linux电源管理(5)_Hibernate和Sleep功能介绍【转】的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 在ubuntu16.04.1配置qemu
- 下一篇: 详解在 Linux 启动时,如何自动执行