1. 程式人生 > >Linux電源管理(5)_Hibernate和Sleep功能介紹

Linux電源管理(5)_Hibernate和Sleep功能介紹

1. 前言

Hibernate和Sleep兩個功能是Linux Generic PM的核心功能,它們的目的是類似的:暫停使用——>儲存上下文——>關閉系統以節電········>恢復系統——>恢復上下文——>繼續使用。

本文以核心向用戶空間提供的介面為突破口,從整體上對這兩個功能進行介紹,並會在後續的文章中,分析它們的實現邏輯和執行動作。

順便感概一下,雖然這些機制在Linux系統中存在很久了(類似的概念也存在於Windows系統中),但以蝸蝸的觀察,它們被使用的頻率並不是很高,特別是在PC上,大多數人在大多數時候選擇直接關閉系統。陰錯陽差的是,在很多嵌入式裝置中,設計者會利用Sleep機制實現熱關機功能,以此減少開機的時間。

2. Hibernate和Sleep相關的術語梳理

蝸蝸在“Generic PM之基本概念和軟體架構”中提到了Linux Generic PM有關的多個詞彙,如Hibernate、Sleep、Suspend、Standby等等,聽起來有些亂,因此在介紹Hibernate和Sleep之前,先來理一下這些詞彙的關係。

▆ Hibernate(冬眠)和Sleep(睡眠)

是Linux電源管理在使用者角度的抽象,是使用者可以看到的實實在在的東西。它們的共同點,是儲存系統執行的上下文後掛起(suspend)系統,並在系統恢復後接著執行,就像什麼事情都沒有發生一樣。它們的不同點,是上下文儲存的位置、系統恢復的觸發方式以及具體的實現機制。

▆ Suspend

有兩個層次的含義。一是Hibernate和Sleep功能在底層實現上的統稱,都是指掛起(Suspend)系統,根據上下文的儲存位置,可以分為Suspend to Disk(STD,即Hibernate,上下文儲存在硬碟/磁碟中)和Suspend to RAM(STR,為Sleep的一種,上下文儲存在RAM中);二是Sleep功能在程式碼級的實現,表現為“kernel/power/suspend.c”檔案。

▆ Standby,是Sleep功能的一個特例,可以翻譯為“打盹”。

正常的Sleep(STR),會在處理完上下文後,由arch-dependent程式碼將CPU置為低功耗狀態(通常為Sleep)。而現實中,根據對功耗和睡眠喚醒時間的不同需求,CPU可能會提供多種低功耗狀態,如除Sleep之外,會提供Standby狀態,該狀態下,CPU處於淺睡眠模式,有任何的風吹草動,就會立即醒來。

▆ Wakeup

這是我們第一次正式的提出Wakeup的概念。我們多次提到恢復系統,其實在核心中稱為Wakeup。表面上,wakeup很簡單,無論是冬眠、睡眠還是打盹,總得有一個刺激讓我們回到正常狀態。但複雜的就是,什麼樣的刺激才能讓我們醒來?

動物界,溫度回升可能是唯一可以讓動物從冬眠狀態醒來的刺激。而踢一腳、鬧鐘響等刺激,則可以讓我們從睡眠狀態喚醒。對於打盹來說,則任何的風吹草動,都可以喚醒。

而在計算機界,冬眠(Hibernate)時,會關閉整個系統的供電,因此想醒來,唯有Power按鈕可用。而睡眠時,為了縮短Wakeup時間,並不會關閉所有的供電,另外,為了較好的使用者體驗,通常會保留某些重要裝置的供電(如鍵盤),那樣這些裝置就可以喚醒系統。

這些刻意保留下來的、可以喚醒系統的裝置,統稱為喚醒源(Wakeup source)。而Wakeup source的選擇,則是PM設計工作(特別是Sleep、Standby等功能)的重點。

經過上面的解釋後,為了統一,蝸蝸會把表述從使用者角度(Hibernate和Sleep)切換為底層實現上(STD、STR和Standby)。

3. 軟體架構及模組彙整

3.1 軟體架構

核心中該部分的軟體架構大概可以分為三個層次,如下圖:

Hibernate & Sleep Architecture

1)API Layer,描述使用者空間API的一個抽象層。

這裡的API有兩類,一類涉及Hibernate和Sleep兩個功能(global APIs),包括實際功能、測試用功能、Debug用功能等,通過sysfs和debugfs兩種形式提供;另一類是Hibernate特有的(STD APIs),通過sysfs和字元裝置兩種形式提供。

2)PM Core,電源管理的核心邏輯層,位於kernel/power/目錄下,包括主功能(main)、STD、STR&Standby以及輔助功能(assistant)等多個子模組。

主功能,主要負責實現global APIs相關的邏輯,為使用者空間提供相應的API;

STD,包括hibernate、snapshot、swap、block_io等子模組,負責實現STD功能和硬體無關的邏輯;

STR&Stanby,包括suspend和suspend_test兩個子模組,負責實現STR、Standby等功能和硬體無關的邏輯。

3)PM Driver,電源管理驅動層,涉及體系結構無關驅動、體系結構有關驅動、裝置模型以及各個裝置驅動等多個軟體模組。

3.2 使用者空間介面

3.2.1 /sys/power/state

state是sysfs中一個檔案,為Generic PM的核心介面,在“kernel/power/main.c”中實現,用於將系統置於指定的Power State(供電模式,如Hibernate、Sleep、Standby等)。不同的電源管理功能,在底層的實現,就是在不同Power State之間切換。

讀取該檔案,返回當前系統支援的Power State,形式為字串。在核心中,有兩種型別的Power State,一種是Hibernate相關的,名稱為“disk”,除“disk”之外,核心在"kernel/power/suspend.c"中通過陣列的形式定義了另外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,只是凍結所有的程序,包括使用者空間程序及核心執行緒。和我們熟知的“冬眠”和“睡眠”相比,就稱為“閉目養神”吧(可想而知,能節省的能量是有限的)。

【注:我們在之前的描述中,並沒有特別描述該State,因為它在較早的核心中,只是Sleep、Hibernate等功能的一部分,只是在近期才獨立出來。另外一個原因是,該state的省電效果不是很理想,所以其引用場景也是有限的。】

▆ standby,即第2章所描述的Standby狀態。

▆ mem,即通常所講的Sleep功能,也是第2章所描述的STR,Suspend to RAM。

▆ disk,即Hibernate功能,也是第2章所描述的STD,Suspend to Disk。

寫入特定的Power State字串,將會把系統置為該模式。

3.2.2 /sys/power/pm_trace

PM Trace用於提供電源管理過程中的Trace記錄,由“CONFIG_PM_TRACE”巨集定義(kernel/power/Kconfig)控制是否編譯進核心,並由“/sys/power/pm_trace”檔案在執行時控制是否使能該功能。

該功能的具體實現是“平臺相關”的,我們這裡暫不描述。

3.2.3 /sys/power/pm_test

PM test用於對電源管理功能的測試,由“CONFIG_PM_DEBUG”巨集定義(kernel/power/Kconfig)控制是否編譯進核心。其核心思想是:

▆ 將電源管理過程按照先後順序,劃分為多個步驟,如core、platform、devices等。這些步驟稱作PM Test Level。

▆ 系統通過一個全域性變數(pm_test_level),儲存系統當前的PM Test Level。該變數的值可以通過”/sys/power/pm_test“檔案獲取及修改。

▆ 在每一個電源管理步驟結束後,插入PM test程式碼,該程式碼以當前執行步驟為引數,會判斷當前的PM Test Level和執行步驟是否一致,如果一致,則說明該步驟執行成功。出於Test考量,執行成功後,系統會列印Test資訊,並在等待一段時間後,退出PM過程。

▆ 開發人員可以通過修改全域性的Test Level,有目的測試所關心的步驟是否執行成功。

上面已經講了,該檔案用於獲取及修改PM Test Level,具體的Level資訊在“kernel/power/main.c”中定義,格式如下(具體的意義,比較簡單,對著相關的程式碼看,非常清晰,這裡就不囉嗦了):

   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功能有關,因此由“CONFIG_PM_SLEEP”巨集定義(kernel/power/Kconfig)控制。它的存在,是為了解決Sleep和Wakeup之間的同步問題。

我們知道,系統睡眠後,可以通過保留的Wakeup source喚醒系統。而在當今的CPU體系中,喚醒系統就是喚醒CPU,而喚醒CPU的唯一途徑,就是Wakeup source產生中斷(核心稱作Wakeup event)。而核心要保證在多種狀態下,Sleep/Wakeup的行為都能正常,如下:

▆ 系統處於sleep狀態時,產生了Wakeup event。此時應該直接喚醒系統。這一點沒有問題。

▆ 系統在進入sleep的過程中,產生了Wakeup event。此時應該放棄進入sleep。

這一點就不那麼容易做到了。例如,當Wakeup event發生在“/sys/power/state”被寫之後、核心執行freeze操作之前。此時使用者空間程式依舊可以處理Wakeup event,或者只是部分處理。而核心卻以為該Event已經被處理,因此並不會放棄此次sleep動作。

這就會造成,Wakeup event發生後,使用者空間程式已經後悔了,不想睡了,但最終還是睡下去了。直到下一個Wakeup event到來。

為了解決上面的問題,核心提供wkaeup_count機制,配合“/sys/power/state”,以實現Sleep過程中的同步。該機制的操作行為如下:

▆ wakeup_count是核心用來儲存當前wakeup event發生的計數。

▆  使用者空間程式在寫入state切換狀態之前,應先讀取wakeup_count並把獲得的count寫回給wakeup_count。

▆ 核心會比對寫回的count和當前的count是否一致,如果不一致,說明在讀取/寫回操作之間,產生了新的的wakeup event,核心就會返回錯誤。

▆ 使用者空間程式檢測到寫入錯誤之後,不能繼續後的動作,需要處理響應的event並伺機再次讀取/寫回wakeup_count。

▆ 如果核心比對一致,會記錄write wakeup_count成功時的event快照,後面繼續suspend動作時,會檢查是否和快照相符,如果不符,會終止suspend。

▆ 使用者空間程式檢測到寫入正確後,可以繼續對state的寫入,以便發起一次狀態切換。而此時是安全的。

蝸蝸會在後續的文章中,詳細描述該機制在核心中的實現邏輯,這裡暫不做進一步說明。

3.2.5 /sys/power/disk

該介面是STD特有的。用於設定或獲取STD的型別。當前核心支援的STD型別包括:

   1: static const char * const hibernation_modes[] = {
   2:         [HIBERNATION_PLATFORM]  = "platform",
   3:         [HIBERNATION_SHUTDOWN]  = "shutdown",
   4:         [HIBERNATION_REBOOT]    = "reboot",
   5: #ifdef CONFIG_SUSPEND
   6:         [HIBERNATION_SUSPEND]   = "suspend",
   7: #endif
   8: };

▆ platform,表示使用平臺特有的機制,處理STD操作,如使用hibernation_ops等。

▆ shutdown,通過關閉系統實現STD,核心會呼叫kernel_power_off介面。

▆ reboot,通過重啟系統實現STD,核心會呼叫kernel_restart介面。

【注:以上兩個kernel_xxx介面的實現,可參考“Generic PM之Reboot過程”。】

▆ suspend,利用STR功能,實現STD。該型別下,STD和STR底層的處理邏輯類似。

3.2.6 /sys/power/image_size

該介面也是STD特有的。我們知道,STD的原理是將當前的執行上下文儲存在系統的disk(如NAND Flash,如硬碟),然後選擇合適的方式關閉或重啟系統。儲存上下文是需要儲存空間的,不光是disk中的儲存空間,也包括位於記憶體的用於交換或緩衝的空間。

而該介面,就是設定或者獲取當前記憶體中需要分配多少空間,用於緩衝需要寫入到disk的資料。單位為byte。

3.2.6 /sys/power/reserverd_size

reserverd_size用於指示預留多少記憶體空間,用於在->freeze() 和 ->freeze_noirq()過程中儲存裝置驅動分配的空間。以免在STD的過程中丟失。

3.2.7 /sys/power/resume

該介面也是STD特有的。正常情況下,在重新開機後,核心會在後期的初始化過程中,讀取儲存在disk中的image,並恢復系統。而該介面,提供了一種在使用者空間手動的讀取image並恢復系統的方法。

通常情況下,該操作出現在系統正常執行的過程中,需要載入並執行另外的image。

3.2.8 debugfs/suspend_status

該介面是以debugfs的形式,向用戶空間提供suspend過程的統計資訊,包括:成功的次數、失敗的次數、freeze失敗的次數等等。

3.2.9 /dev/snapshot

該介面也是STD特有的。它通過字元裝置的形式,向用戶空間提供software的STD操作。我們會在後續的文章中詳細描述。