1. 程式人生 > >Linux系統中電源管理框架詳解

Linux系統中電源管理框架詳解

轉載地址:http://www.wowotech.net/linux_kenrel/suspend_and_resume.html

1. 前言

Linux核心提供了三種Suspend: Freeze、Standby和STR(Suspend to RAM),在使用者空間向”/sys/power/state”檔案分別寫入”freeze”、”standby”和”mem”,即可觸發它們。

核心中,Suspend及Resume過程涉及到PM Core、Device PM、各個裝置的驅動、Platform dependent PM、CPU control等多個模組,涉及了console switch、process freeze、CPU hotplug、wakeup處理等過個知識點。就讓我們跟著核心程式碼,一一見識它們吧。

2. Suspend功能有關的程式碼分佈

核心中Suspend功能有關的程式碼包括PM core、Device PM、Platform PM等幾大塊,具體如下:

1)PM Core

kernel/power/main.c----提供使用者空間介面(/sys/power/state)

kernel/power/suspend.c----Suspend功能的主邏輯

kernel/power/suspend_test.c----Suspend功能的測試邏輯

kernel/power/console.c----Suspend過程中對控制檯的處理邏輯

kernel/power/process.c----Suspend過程中對程序的處理邏輯

2)Device PM

裝置驅動----具體裝置驅動的位置,不再涉及。

3)Platform dependent PM

include/linux/suspend.h----定義platform dependent PM有關的操作函式集

arch/xxx/mach-xxx/xxx.c或者

arch/xxx/plat-xxx/xxx.c----平臺相關的電源管理操作

3. suspend&resume過程概述

下面圖片對Linux suspend&resume過程做了一個概述,讀者可以順著這個流程閱讀核心原始碼。具體的說明,可以參考後面的程式碼分析。

suspend_flow

4. 程式碼分析

4.1 suspend入口

在使用者空間執行如下操作:

echo "freeze" > /sys/power/state

echo "standby" > /sys/power/state

echo "mem" > /sys/power/state

會通過sysfs觸發suspend的執行,相應的處理程式碼如下:

  1. staticssize_t state_store(struct kobject *kobj,struct kobj_attribute *attr,
  2. constchar*buf,size_t n)
  3. {
  4. suspend_state_t state;
  5. int error;
  6. error = pm_autosleep_lock();
  7. if(error)
  8. return error;
  9. if(pm_autosleep_state()> PM_SUSPEND_ON){
  10. error =-EBUSY;
  11. goto out;
  12. }
  13. state = decode_state(buf, n);
  14. if(state < PM_SUSPEND_MAX)
  15. error = pm_suspend(state);
  16. elseif(state == PM_SUSPEND_MAX)
  17. error = hibernate();
  18. else
  19. error =-EINVAL;
  20. out:
  21. pm_autosleep_unlock();
  22. return error ? error : n;
  23. }
  24. power_attr(state);

power_attr定義了一個名稱為state的attribute檔案,該檔案的store介面為state_store,該介面在lock住autosleep功能後,解析使用者傳入的buffer(freeze、standby or mem),轉換成state引數。

state引數的型別為suspend_state_t,在include\linux\suspend.h中定義,為電源管理狀態在核心中的表示。具體如下:
  1. typedefint __bitwise suspend_state_t;
  2. #define PM_SUSPEND_ON ((__force suspend_state_t)0)
  3. #define PM_SUSPEND_FREEZE ((__force suspend_state_t)1)
  4. #define PM_SUSPEND_STANDBY ((__force suspend_state_t)2)
  5. #define PM_SUSPEND_MEM ((__force suspend_state_t)3)
  6. #define PM_SUSPEND_MIN PM_SUSPEND_FREEZE
  7. #define PM_SUSPEND_MAX ((__force suspend_state_t)4)

根據state的值,如果不是(PM_SUSPEND_MAX,對應hibernate功能),則呼叫pm_suspend介面,進行後續的處理。 

pm_suspend在kernel/power/suspend.c定義,處理所有的suspend過程。  

4.2 pm_suspend & enter_state

pm_suspend的實現非常簡單,簡單的做一下引數合法性判斷,直接呼叫enter_state介面,如下:

  1. int pm_suspend(suspend_state_t state)
  2. {
  3. int error;
  4. if(state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
  5. return-EINVAL;
  6. error = enter_state(state);
  7. if(error){
  8. suspend_stats.fail++;
  9. dpm_save_failed_errno(error);
  10. }else{
  11. suspend_stats.success++;
  12. }
  13. return error;
  14. }

enter_state程式碼為:

  1. staticint enter_state(suspend_state_t state)
  2. {
  3. int error;
  4. if(!valid_state(state))
  5. return-ENODEV;
  6. if(!mutex_trylock(&pm_mutex))
  7. return-EBUSY;
  8. if(state == PM_SUSPEND_FREEZE)
  9. freeze_begin();
  10. printk(KERN_INFO "PM: Syncing filesystems ... ");
  11. sys_sync();
  12. printk("done.\n");
  13. pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
  14. error = suspend_prepare(state);
  15. if(error)
  16. gotoUnlock;
  17. if(suspend_test(TEST_FREEZER))
  18. gotoFinish;
  19. pr_debug("PM: Entering %s sleep\n", pm_states[state]);
  20. pm_restrict_gfp_mask();
  21. error = suspend_devices_and_enter(state);
  22. pm_restore_gfp_mask();
  23. Finish:
  24. pr_debug("PM: Finishing wakeup.\n");
  25. suspend_finish();
  26. Unlock:
  27. mutex_unlock(&pm_mutex);
  28. return error;
  29. }

主要工作包括:

a)呼叫valid_state,判斷該平臺是否支援該電源狀態。

suspend的最終目的,是讓系統進入可恢復的掛起狀態,而該功能必須有平臺相關程式碼的參與才能完成,因此核心PM Core就提供了一系列的回撥函式(封裝在platform_suspend_ops中),讓平臺程式碼(如arch/arm/mach-xxx/pm.c)實現,然後由PM Core在合適的時機呼叫。這些回撥函式包含一個valid函式,就是用來告知PM Core,支援哪些state。

最後看一下valid_state的實現(刪除了無關程式碼):
  1. bool valid_state(suspend_state_t state)
  2. {
  3. if(state == PM_SUSPEND_FREEZE){
  4. returntrue;
  5. }
  6. /*
  7. * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
  8. * support and need to be valid to the lowlevel
  9. * implementation, no valid callback implies that none are valid.
  10. */
  11. return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
  12. }

如果是freeze,無需平臺程式碼參與即可支援,直接返回true。對於standby和mem,則需要呼叫suspend_ops的valid回掉,由底層平臺程式碼判斷是否支援。 

b)加互斥鎖,只允許一個例項處理suspend。

c)如果state是freeze,呼叫freeze_begin,進行suspend to freeze相關的特殊動作。我會在後面統一分析freeze的特殊動作,這裡暫不描述。

d)列印提示資訊,同步檔案系統。

e)呼叫suspend_prepare,進行suspend前的準備,主要包括switch console和process&thread freezing。如果失敗,則終止suspend過程。

f)然後,呼叫suspend_devices_and_enter介面,該介面負責suspend和resume的所有實際動作。前半部分,suspend console、suspend device、關中斷、呼叫平臺相關的suspend_ops使系統進入低功耗狀態。後半部分,在系統被事件喚醒後,處理相關動作,呼叫平臺相關的suspend_ops恢復系統、開中斷、resume device、resume console。

g)最後,呼叫suspend_finish,恢復(或等待恢復)process&thread,還原console。  

4.3 suspend_prepare

suspend_prepare的程式碼如下:
  1. staticint suspend_prepare(suspend_state_t state)
  2. {
  3. int error;
  4. if(need_suspend_ops(state)&&(!suspend_ops ||!suspend_ops->enter))
  5. return-EPERM;
  6. pm_prepare_console();
  7. error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
  8. if(error)
  9. gotoFinish;
  10. error = suspend_freeze_processes();
  11. if(!error)
  12. return0;
  13. suspend_stats.failed_freeze++;
  14. dpm_save_failed_step(SUSPEND_FREEZE);
  15. Finish:
  16. pm_notifier_call_chain(PM_POST_SUSPEND);
  17. pm_restore_console();
  18. return error;
  19. }

主要工作為:

a)檢查suspend_ops是否提供了.enter回撥,沒有的話,返回錯誤。

b)呼叫pm_prepare_console,將當前console切換到一個虛擬console並重定向核心的kmsg(需要的話)。該功能稱作VT switch,後面我會在稍微詳細的介紹一下,但Linux控制檯子系統是相當複雜的,更具體的分析,要在控制檯子系統的分析文章中說明。

c)呼叫pm_notifier_call_chain,傳送suspend開始的訊息(PM_SUSPEND_PREPARE),後面會詳細描述。

d)呼叫suspend_freeze_processes,freeze使用者空間程序和一些核心執行緒。該功能稱作freezing-of-tasks,我會專門用一篇文章去分析它。本文就不再詳細說明了。

e)如果freezing-of-tasks失敗,呼叫pm_restore_console,將console切換回原來的console,並返回錯誤,以便能終止suspend。  

4.4 suspend_devices_and_enter

suspend_devices_and_enter的過程較為複雜,程式碼實現如下:
  1. int suspend_devices_and_enter(suspend_state_t state)
  2. {
  3. int error;
  4. bool wakeup =false;
  5. if(need_suspend_ops(state)&&!suspend_ops)
  6. return-ENOSYS;
  7. trace_machine_suspend(state);
  8. if(need_suspend_ops(state)&& suspend_ops->begin){
  9. error = suspend_ops->begin(state);
  10. if(error)
  11. gotoClose;
  12. }
  13. suspend_console();
  14. ftrace_stop();
  15. suspend_test_start();
  16. error = dpm_suspend_start(PMSG_SUSPEND);
  17. if(error){
  18. printk(KERN_ERR "PM: Some devices failed to suspend\n");
  19. gotoRecover_platform;
  20. }
  21. suspend_test_finish("suspend devices");
  22. if(suspend_test(TEST_DEVICES))
  23. gotoRecover_platform;
  24. do{
  25. error = suspend_enter(state,&wakeup);
  26. }while(!error &&!wakeup && need_suspend_ops(state)
  27. && suspend_ops->suspend_again && suspend_ops->suspend_again());
  28. Resume_devices:
  29. suspend_test_start();
  30. dpm_resume_end(PMSG_RESUME);
  31. suspend_test_finish("resume devices");
  32. ftrace_start();
  33. resume_console();
  34. Close:
  35. if(need_suspend_ops(state)&& suspend_ops->end)
  36. suspend_ops->end();
  37. trace_machine_suspend(PWR_EVENT_EXIT);
  38. return error;
  39. Recover_platform:
  40. if(need_suspend_ops(state)&& suspend_ops->recover)
  41. suspend_ops->recover();
  42. gotoResume_devices;
  43. }

a)再次檢查平臺程式碼是否需要提供以及是否提供了suspend_ops。

b)呼叫suspend_ops的begin回撥(有的話),通知平臺程式碼,以便讓其作相應的處理(需要的話)。可能失敗,需要跳至Close處執行恢復操作(suspend_ops->end)。

c)呼叫suspend_console,掛起console。該介面由"kernel\printk.c"實現,主要是hold住一個lock,該lock會阻止其它程式碼訪問console。

d)呼叫ftrace_stop,停止ftrace功能。ftrace是一個很有意思的功能,後面再介紹。

e)呼叫dpm_suspend_start,呼叫所有裝置的->prepare和->suspend回撥函式(具體可參考“Linux電源管理(4)_Power Management Interface”的描述),suspend需要正常suspend的裝置。suspend device可能失敗,需要跳至 Recover_platform,執行recover操作(suspend_ops->recover)。

f)以上都是suspend前的準備工作,此時,呼叫suspend_enter介面,使系統進入指定的電源狀態。該介面的內容如下:
  1. staticint suspend_enter(suspend_state_t state,bool*wakeup)
  2. {
  3. int error;
  4. if(need_suspend_ops(state)&& suspend_ops->prepare){
  5. error = suspend_ops->prepare();
  6. if(error)
  7. gotoPlatform_finish;
  8. }
  9. error = dpm_suspend_end(PMSG_SUSPEND);
  10. if(error){
  11. printk(KERN_ERR "PM: Some devices failed to power down\n");
  12. gotoPlatform_finish;
  13. }
  14. if(need_suspend_ops(state)&& suspend_ops->prepare_late){
  15. error = suspend_ops->prepare_late();
  16. if(error)
  17. gotoPlatform_wake;
  18. }
  19. if(suspend_test(TEST_PLATFORM))
  20. gotoPlatform_wake;
  21. /*
  22. * PM_SUSPEND_FREEZE equals
  23. * frozen processes + suspended devices + idle processors.
  24. * Thus we should invoke freeze_enter() soon after
  25. * all the devices are suspended.
  26. */
  27. if(state == PM_SUSPEND_FREEZE){
  28. freeze_enter();
  29. gotoPlatform_wake;
  30. }
  31. error = disable_nonboot_cpus();
  32. if(error || suspend_test(TEST_CPUS))
  33. gotoEnable_cpus;
  34. arch_suspend_disable_irqs();
  35. BUG_ON(!irqs_disabled());
  36. error = syscore_suspend();
  37. if(!error){
  38. *wakeup = pm_wakeup_pending();
  39. if(!(suspend_test(TEST_CORE)||*wakeup)){
  40. error = suspend_ops->enter(state);
  41. events_check_enabled =false;
  42. }
  43. syscore_resume();
  44. }
  45. arch_suspend_enable_irqs();
  46. BUG_ON(irqs_disabled());
  47. Enable_cpus:
  48. enable_nonboot_cpus();
  49. Platform_wake:
  50. if(need_suspend_ops(state)&& suspend_ops->wake)
  51. suspend_ops->wake();
  52. dpm_resume_start(PMSG_RESUME);
  53. Platform_finish:
  54. if(need_suspend_ops(state)&& suspend_ops->finish)
  55. suspend_ops->finish();
  56. return error;
  57. }

        f1)該介面處理完後,會通過返回值告知是否enter成功,同時通過wakeup指標,告知呼叫者,是否有wakeup事件發生,導致電源狀態切換失敗。

        f2)呼叫suspend_ops的prepare回撥(有的話),通知平臺程式碼,以便讓其在即將進行狀態切換之時,再做一些處理(需要的話)。該回調可能失敗(平臺程式碼出現意外),失敗的話,需要跳至Platform_finish處,呼叫suspend_ops的finish回撥,執行恢復操作。

        f3)呼叫dpm_suspend_end,呼叫所有裝置的->suspend_late和->suspend_noirq回撥函式(具體可參考“Linux電源管理(4)_Power Management Interface”的描述),suspend late suspend裝置和需要在關中斷下suspend的裝置。需要說明的是,這裡的noirq,是通過禁止所有的中斷線的形式,而不是通過關全域性中斷的方式。同樣,該操作可能會失敗,失敗的話,跳至Platform_finish處,執行恢復動作。

        f4)呼叫suspend_ops的prepare_late回撥(有的話),通知平臺程式碼,以便讓其在最後關頭,再做一些處理(需要的話)。該回調可能失敗(平臺程式碼出現意外),失敗的話,需要跳至Platform_wake處,呼叫suspend_ops的wake回撥,執行device的resume、呼叫suspend_ops的finish回撥,執行恢復操作。

        f5)如果是suspend to freeze,執行相應的操作,包括凍結程序、suspended devices(引數為PM_SUSPEND_FREEZE)、cpu進入idle。如果有任何事件使CPU從idle狀態退出,跳至Platform_wake處,執行wake操作。

        f6)呼叫disable_nonboot_cpus,禁止所有的非boot cpu。也會失敗,執行恢復操作即可。

        f7)呼叫arch_suspend_disable_irqs,關全域性中斷。如果無法關閉,則為bug。

        f8)呼叫syscore_suspend,suspend system core。同樣會失敗,執行恢復操作即可。有關syscore,我會在另一篇文章中詳細描述。

        f9)如果很幸運,以上操作都成功了,那麼,切換吧。不過,別高興太早,還得呼叫pm_wakeup_pending檢查一下,這段時間內,是否有喚醒事件發生,如果有就要終止suspend。

        f10)如果一切順利,呼叫suspend_ops的enter回撥,進行狀態切換。這時,系統應該已經suspend了……

        f11)suspend過程中,喚醒事件發生,系統喚醒,該函式接著執行resume動作,並最終返回。resume動作基本上是suspend的反動作,就不再繼續分析了。

        f12)或者,由於意外,suspend終止,該函式也會返回。

g)suspend_enter返回,如果返回原因不是發生錯誤,且不是wakeup事件。則呼叫suspend_ops的suspend_again回撥,檢查是否需要再次suspend。再什麼情況下要再次suspend呢?需要看具體的平臺了,誰知道呢。

相關推薦

Linux系統電源管理框架

轉載地址:http://www.wowotech.net/linux_kenrel/suspend_and_resume.html 1. 前言 Linux核心提供了三種Suspend: Freeze、Standby和STR(Suspend to RAM),在使用者空間向”

Linux系統實現RAID卷

   在Linux系統中做RAID,磁碟陣列的裝置可以是一塊磁碟中的三個以上的分割槽,也可以是三塊或以上的磁碟。本文主要以幾塊磁碟為例,來實現在RAID5。實驗環境:    系統中有一塊磁碟sda,新新增6塊SCSI磁碟,分別為sdb,sdc,sdd,sde,sdf,sdg

Linux系統關於ls命令

-d, –directory 將目錄象檔案一樣顯示,而不是顯示其下的檔案。 -D, –dired 產生適合 Emacs 的 dired 模式使用的結果 -f 對輸出的檔案不進行排序,-aU 選項生效,-lst 選項失效 -F, –classify 加上檔案型別的指示符號 (*/=@| 其中一個) –forma

linux--系統啟動及安裝過程

linux啟動先通過一張圖來簡單了解下整個系統啟動的流程,整個過程基本可以分為POST-->BIOS-->MBR(GRUB)-->Kernel-->Init-->Runlevel本文出自 “運維自動化” 博客,請務必保留此出處http://shower.blog.51cto.co

linux系統下pk10網站搭建

用戶 rhel6 增加 添加 上下 ORC security 一行 .sh 一.設置pk10網站搭建(企 娥:217 1793 408)系統root用戶下 #vi /etc/redhat-release //操作系統,系統只支持Red Hat Enterprise Linu

Linux系統下的權限-1

實例 用戶 常用 表示 user chm 查看 運行 color 在Linux系統根下,通過使用ll 命令查看得出:Linux中常用權限有 r w x 如圖所示,權限共9位構成。(註:“-” 也表示一位)權限是賦給誰的?答:用戶(user)組(group)其他人(o

Linux系統下的權限-2

權限 rwx root inux 增加 使用命令 命令 文件的 如果 chmod 的用法:chmod 給誰賦於什麽權限 權限賦給的對象備註:誰是指用戶、組、其他人;權限賦給的對象是指目錄或文件chmod命令舉例如下: 使用字符賦予權限 [root@agan ~]

SAP系統信用控制功能

1、引言 現金銷售和預收款銷售一般指發生在壟斷性行業,多數企業不得不面對產品賒銷的兩難選擇,賒銷是把雙刃劍,如果不賒銷,不能迅速擴大銷量,從而影響企業的成長速度;如果賒銷,則生意雖然做大,特別國內信用制度還未完善情況下,大筆壞帳也跑出來了,嚴重降低資金週轉率和

Linux系統常用磁碟陣列RAID5

RAID5最少由3塊硬碟組成,每個硬碟容量一樣,資料儲存於磁碟陣列中的每個硬碟,其中一塊硬碟儲存資料校驗位,當丟失其中的一位時,RAID5能通過演算法,利用其他兩位資料將丟失的資料進行計算還原,因此RAID5最多隻能允許一塊硬碟損壞,可見磁碟利用率是(N-1)/N,資料的安全性得以保障,一般大多數人選擇用RA

Linux系統Shutdown命令定時關機

Linux系統下的shutdown命令用於安全的關閉/重啟計算機,它不僅可以方便的實現定時關機,還可以由使用者決定關機時的相關引數。在執行shutdown命令時,系統會給每個終端(使用者)傳送一條屏顯,提示關機操作。定時關機只需要一個簡單的引數,既可以是倒計時,也可以是確切的

Linux 系統設定 : alias & unalias 命令

alias命令用來設定指令的別名。我們可以使用該命令可以將一些較長的命令進行簡化。使用alias時,使用者必須使用單引號''將原來的命令引起來,防止特殊字元導致錯誤。 alias命令的作用只侷限於該次登入的操作。若要每次登入都能夠使用這些命令別名,則可將相應的alias命令

Linux 系統設定 : clock & clockdiff 命令

Linux clock命令用於調整 RTC 時間。 RTC 是電腦內建的硬體時間,執行這項指令可以顯示現在時刻,調整硬體時鐘的時間,將系統時間設成與硬體時鐘之時間一致,或是把系統時間回存到硬體時鐘。 語法 clock [--adjust][--debug][--dire

LR監控Linux系統伺服器效能監控指標

一、常用監控指標: 從LR-System Resource Graphs裡面右鍵add measurement,填寫linux機器的IP, 出現所有unix/linux的計數器,包括cpu的,mem的,disk,network的。 幾個常用的監控指標: aver

Linux系統防火牆管理-iptables

一.iptables概述 iptables(網路過濾器)是一個工作於使用者空間的防火牆應用軟體。iptable是Linux下的資料包過濾軟體 。 iptables有三張表五條鏈: 1.Filter:資料包流通是否訪問Linux核心的表 Filter包含三條鏈input、 forward

Linux系統下apt-get命令

常用的APT命令引數: apt-cache search package 搜尋包 apt-cache show package 獲取包的相關資訊,如說明、大小、版本等 sudo apt-get install package 安裝包 sudo apt-get instal

雲端計算:Linux運維核心管理命令

雲端計算:Linux運維核心管理命令詳解 想做好運維工作,人先要學會勤快; 居安而思危,勤記而補拙,方可不斷提高; 別人資料不論你用著再如何爽那也是別人的; 自己總結東西是你自身特有的一種思想與理念的展現; 精髓不是看出來的,精髓是記出來的; 請同學們在學習

Linux系統呼叫-- mount/umount函式

【 mount/umount系統呼叫】         功能描述: mount掛上檔案系統,umount執行相反的操作。    用法:   #include <sys/mount.h> int mount(const char *source, const ch

Ubuntu Linux系統下apt-get命令 轉貼

常用的APT命令引數: apt-cache search package 搜尋包 apt-cache show package 獲取包的相關資訊,如說明、大小、版本等 sudo apt-get install package 安裝包 sudo apt-get install

linux-3.4 電源管理框架(1)

1. linux 中支援的電源管理 省電模式 'standby' (Power-On Suspend) 顯示屏斷電,主機通電 ================= 待機 'mem' (Suspend-to-RAM) 掛起到記憶體,需要外部中

Linux 系統如何管理 systemd 服務

在上一篇文章《Linux的執行級別與目標》中,我介紹過 Linux 用 systemd 來取代 init 作為系統的初始化程序。儘管這一改變引來了很多爭議,但大多數發行版,包括 RedHat、Fedora、CentOS、Debian、Ubuntu、openSUSE、Arch 等等都已經做出了調整。不管是哪一個