• Linux Suspend过程【转】


    转自:https://blog.csdn.net/zifeng274059226/article/details/50352826

    转载地址: http://blog.csdn.net/chen198746/article/details/15809363
    1. Linux Suspend简介

        Linux Suspend主要有以下三步:

       1) 冻结用户态进程和内核态任务 
       2) 调用注册的设备的suspend的回调函数,顺序是按照注册顺序 
       3) 休眠核心设备和使CPU进入休眠态。
       冻结进程(suspend_freeze_processes)是内核把进程列表中所有的进程的状态都设置为停止,并且保存所有进程的上下文。 当这些进程被解冻(suspend_thaw_processes)的时候,他们是不知道自己被冻结过的,只是简单的继续执行。如何让Linux进入Suspend呢?用户可以通过读写sys文件/sys /power/state 是实现控制系统进入休眠,比如:

       # echo standby > /sys/power/state

    2. Suspend流程

        Suspend主要流程如下图所示:

    3. enter_state(PM_SUSPEND_MEM)

          其主要功能如下:

         1) suspend_prepare:   准备进入suspend,并冻结所有进程

         2) suspend_devices_and_enter: suspend所有外设,并进入sleep状态,只有当唤醒时,此函数才返回

         3) suspend_finish: suspend结束,并被唤醒

           

         enter_state代码如下:  

    [cpp] view plaincopy
     
     
     
    1. // kernel/kernel/power/suspend.c  
    2. int enter_state(suspend_state_t state)  
    3. {  
    4.     int error;  
    5.     if (!valid_state(state))  
    6.         return -ENODEV;  
    7.   
    8.     if (!mutex_trylock(&pm_mutex))  
    9.         return -EBUSY;  
    10.   
    11. #ifdef CONFIG_SUSPEND_SYNC_WORKQUEUE  
    12.     suspend_sys_sync_queue();  
    13. #else  
    14.     sys_sync();  
    15.     printk("done. ");  
    16. #endif  
    17.   
    18.     pr_debug("PM: Preparing system for %s sleep ", pm_states[state]);  
    19.   
    20.     error = suspend_prepare(); //准备进入suspend,并冻结所有进程  
    21.     if (error)  
    22.         goto Unlock;  
    23.   
    24.     if (suspend_test(TEST_FREEZER))  
    25.         goto Finish;  
    26.   
    27.     pr_debug("PM: Entering %s sleep ", pm_states[state]);  
    28.     pm_restrict_gfp_mask();  
    29.     error = suspend_devices_and_enter(state); // suspend外部设备  
    30.     pm_restore_gfp_mask();  
    31.   
    32.  Finish:  
    33.     pr_debug("PM: Finishing wakeup. ");  
    34.     suspend_finish();  // 结束suspend,并被唤醒  
    35.  Unlock:  
    36.     mutex_unlock(&pm_mutex);  
    37.     return error;  
    38. }  


    3.1 准备并冻结进程(suspend_prepare)

          在suspend_prepare()中它将完成以下任务:

          1) 给suspend分配一个虚拟终端来输出信息;

          2) 然后广播一个系统要进入suspend的Notify;

          3) 关闭掉用户态的helper进程;

          4) 最后调用suspend_freeze_processes()冻结所有的进程,这里将保存所有进程 当前的状态,也许有一些进程会拒绝进入冻结状态,当有这样的进程存在的时候,会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程。

          其详细代码如下:

    [cpp] view plaincopy
     
     
     
    1. static int suspend_prepare(void)  
    2. {  
    3.     int error;  
    4.   
    5.     if (!suspend_ops || !suspend_ops->enter)  
    6.         return -EPERM;  
    7.   
    8.     pm_prepare_console();  // 分配一个console  
    9.   
    10.     error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); // 发送suspend notify  
    11.     if (error)  
    12.         goto Finish;  
    13.   
    14.     error = usermodehelper_disable(); // disable用户态的helper进程  
    15.     if (error)  
    16.         goto Finish;  
    17.   
    18.     error = suspend_freeze_processes(); // 冻结所有进程  
    19.     if (!error)  
    20.         return 0;  
    21.   
    22.     suspend_thaw_processes();  
    23.     usermodehelper_enable();  
    24.  Finish:  
    25.     pm_notifier_call_chain(PM_POST_SUSPEND);  
    26.     pm_restore_console();  
    27.     return error;  
    28. }  


    3.2 Suspend外部设备(suspend_devices_and_enter)

           现在, 所有的进程(也包括workqueue/kthread) 都已经停止了,内核态进程有可能在停止的时候握有一些信号量, 所以如果这时候在外设里面去解锁这个信号量有可能会发生死锁, 所以在外设的suspend()函数里面进行lock/unlock锁要非常小心,建议设计时不要在suspend()里面等待锁。而且因为suspend的时候,有一些Log是无法输出的,所以一旦出现问题,非常难调试。
           suspend_devices_and_enter的主要功能为:

           1) suspend_console: Suspend console子系统,即printk将不能打印了

           2) dpm_suspend_start: Suspend所有非系统设备,即调用所有注册设备的suspend回调函数

           3) suspend_enter: 使系统进入要求的sleep状态,然后停在这儿,只有当系统被中断或者其他事件唤醒时,此函数才返回

               以下函数只有当wakeup时才被执行:

           4) dpm_resume_end:  resume所有非系统设备,即执行所有注册设备的resume回调函数

           5) resume_console: resume console子系统,即printk可用了

           详细代码如下所示:

                kernel/kernel/power/suspend.c

    [cpp] view plaincopy
     
     
     
    1. int suspend_devices_and_enter(suspend_state_t state)  
    2. {  
    3.     int error;  
    4.         /* suspend_pos通过suspend_set_ops来进行注册, 
    5.            它在kernel/arch/arm/mach-xx/pm.c中定义,其函数名 
    6.            可能为xx_pm_ops,例子如下: 
    7.        static struct platform_suspend_ops rk30_pm_ops = { 
    8.         .enter      = xx_pm_enter, 
    9.         .valid      = suspend_valid_only_mem, 
    10.         .prepare    = xx_pm_prepare, 
    11.         .finish     = xx_pm_finish, 
    12.        }; 
    13.     */  
    14.   
    15.     if (!suspend_ops)  
    16.         return -ENOSYS;  
    17.   
    18.     trace_machine_suspend(state);  
    19.     if (suspend_ops->begin) {  
    20.         error = suspend_ops->begin(state);  
    21.         if (error)  
    22.             goto Close;  
    23.     }  
    24.     suspend_console(); // suspend console子系统,printk将不能打印了  
    25.     suspend_test_start();  
    26.     error = dpm_suspend_start(PMSG_SUSPEND);  // suspend所有非系统设备  
    27.                                                   // 即执行所有设备的suspend回调函数  
    28.     if (error) {  
    29.         printk(KERN_ERR "PM: Some devices failed to suspend ");  
    30.         goto Recover_platform;  
    31.     }  
    32.     suspend_test_finish("suspend devices");  
    33.     if (suspend_test(TEST_DEVICES))  
    34.         goto Recover_platform;  
    35.   
    36.     error = suspend_enter(state); // 系统进入要求的sleep状态,  
    37.                                       // 只有当wakeup时,此函数才返回  
    38.   
    39.  Resume_devices:  
    40.     suspend_test_start();  
    41.     dpm_resume_end(PMSG_RESUME);  // resume所有非系统设备  
    42.                                       // 即执行所有设备的resume回调函数  
    43.     suspend_test_finish("resume devices");  
    44.     resume_console();             // resume console子系统,即printk可用了  
    45.  Close:  
    46.     if (suspend_ops->end)  
    47.         suspend_ops->end();  
    48.     trace_machine_suspend(PWR_EVENT_EXIT);  
    49.     return error;  
    50.   
    51.  Recover_platform:  
    52.     if (suspend_ops->recover)  
    53.         suspend_ops->recover();  
    54.     goto Resume_devices;  
    55. }  

    3.2.1 suspend_console

            Suspend console子系统,即printk将不能打印了

    [cpp] view plaincopy
     
     
     
    1. void suspend_console(void)  
    2. {  
    3.     if (!console_suspend_enabled)  
    4.         return;  
    5.     printk("Suspending console(s) (use no_console_suspend to debug) ");  
    6.     console_lock();  
    7.     console_suspended = 1;  
    8.     up(&console_sem);  
    9. }  

    3.2.2 dpm_suspend_start  (PMSG_SUSPEND)

            Suspend所有非系统设备,即调用所有注册设备的suspend回调函数

    [cpp] view plaincopy
     
     
     
    1. /** 
    2.  * dpm_suspend_start - Prepare devices for PM transition and suspend them. 
    3.  * @state: PM transition of the system being carried out. 
    4.  * 
    5.  * Prepare all non-sysdev devices for system PM transition and execute "suspend" 
    6.  * callbacks for them. 
    7.  */  
    8. int dpm_suspend_start(pm_message_t state)  
    9. {  
    10.     int error;  
    11.   
    12.     error = dpm_prepare(state);  // 根据dpm_list生成dpm_prepared_list  
    13.     if (!error)  
    14.         error = dpm_suspend(state); //根据dpm_prepared_list生成dpm_suspended_list  
    15.     return error;  
    16. }  

      

    3.2.3 suspend_enter 

      

        使系统进入要求的sleep状态,然后停在这儿,只有当系统被中断或者其他事件唤醒时,此函数才返回,其详细代码如下:

    [cpp] view plaincopy
     
     
     
    1. /** 
    2.  *  suspend_enter - enter the desired system sleep state. 
    3.  *  @state:     state to enter 
    4.  * 
    5.  *  This function should be called after devices have been suspended. 
    6.  */  
    7. static int suspend_enter(suspend_state_t state)  
    8. {  
    9.     int error;  
    10.   
    11.     if (suspend_ops->prepare) {  
    12.         error = suspend_ops->prepare(); //即执行xx_pm_prepare  
    13.         if (error)  
    14.             goto Platform_finish;  
    15.     }  
    16.   
    17.     error = dpm_suspend_noirq(PMSG_SUSPEND); //使所有外设驱动不再接收中断  
    18.     if (error) {  
    19.         printk(KERN_ERR "PM: Some devices failed to power down ");  
    20.         goto Platform_finish;  
    21.     }  
    22.   
    23.     if (suspend_ops->prepare_late) {  
    24.         error = suspend_ops->prepare_late();  
    25.         if (error)  
    26.             goto Platform_wake;  
    27.     }  
    28.   
    29.     if (suspend_test(TEST_PLATFORM))  
    30.         goto Platform_wake;  
    31.   
    32.     error = disable_nonboot_cpus(); // 停止非启动CPU  
    33.     if (error || suspend_test(TEST_CPUS))  
    34.         goto Enable_cpus;  
    35.   
    36.     arch_suspend_disable_irqs(); // 关闭中断  
    37.     BUG_ON(!irqs_disabled());  
    38.   
    39.     error = syscore_suspend();  // 执行注册在syscore_ops_list的syscore_ops的suspend函数  
    40.     if (!error) {  
    41.         if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {  
    42.             error = suspend_ops->enter(state); // KEY: 即执行xx_pm_enter,唤醒时才返回  
    43.             events_check_enabled = false;  
    44.         }  
    45.         syscore_resume(); // 执行注册在syscore_ops_list的syscore_ops的resume函数  
    46.     }  
    47.   
    48.     arch_suspend_enable_irqs(); // 打开中断  
    49.     BUG_ON(irqs_disabled());  
    50.   
    51.  Enable_cpus:  
    52.     enable_nonboot_cpus(); // 启动非启动CPU  
    53.   
    54.  Platform_wake:  
    55.     if (suspend_ops->wake)  
    56.         suspend_ops->wake();  
    57.   
    58.     dpm_resume_noirq(PMSG_RESUME); //使所有外设驱动接收中断  
    59.   
    60.  Platform_finish:  
    61.     if (suspend_ops->finish)  
    62.         suspend_ops->finish(); //即执行xx_pm_finish  
    63.   
    64.     return error;  
    65. }  

      

    3.2.4 dpm_resume_end  (PMSG_RESUME)

            resume所有非系统设备,即执行所有注册设备的resume回调函数

    [cpp] view plaincopy
     
     
     
    1. /** 
    2.  * dpm_resume_end - Execute "resume" callbacks and complete system transition. 
    3.  * @state: PM transition of the system being carried out. 
    4.  * 
    5.  * Execute "resume" callbacks for all devices and complete the PM transition of 
    6.  * the system. 
    7.  */  
    8. void dpm_resume_end(pm_message_t state)  
    9. {  
    10.     dpm_resume(state); //根据dpm_suspended_list生成dpm_prepared_list  
    11.     dpm_complete(state); //根据dpm_prepared_list生成dpm_list  
    12. }  

    3.2.5 resume_console

             resume console子系统,即printk可用了

    [cpp] view plaincopy
     
     
     
    1. void resume_console(void)  
    2. {  
    3.     if (!console_suspend_enabled)  
    4.         return;  
    5.     down(&console_sem);  
    6.     console_suspended = 0;  
    7.     console_unlock();  
    8. }  

    3.3 Suspend结束(suspend_finish)

        其主要功能如下(它是suspend_prepare的逆过程):

          1) 解冻所有进程;

          2) 打开用户态helper进程;

          3) 广播系系统suspend结束的Notify;

          4) 释放分配的虚拟终端。

         其详细代码如下:
     

    [cpp] view plaincopy
     
     
     
    1. static void suspend_finish(void)  
    2. {  
    3.     suspend_thaw_processes();  //解冻所有进程  
    4.     usermodehelper_enable();   // 打开用户态helper进程  
    5.     pm_notifier_call_chain(PM_POST_SUSPEND); // 广播系系统suspend结束的Notify  
    6.     pm_restore_console();  // 释放分配的虚拟终端  
    7. }  
  • 相关阅读:
    codeforces 455C 并查集
    poj 3501 Escape from Enemy Territory 预处理+二分+bfs
    POJ 2110 Mountain Walking 二分+bfs
    poj1637 Sightseeing tour 混合图欧拉回路判定
    ubuntu禁用super(win)键
    win10 ubuntu双系统安装后无法引导进入ubuntu
    python2限制函数传入的关键字参数
    python限制函数执行时间
    python classmethod 和 staticmethod的区别
    centos sendmail 启动慢
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/12562823.html
Copyright © 2020-2023  润新知