zoukankan      html  css  js  c++  java
  • 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. }  
  • 相关阅读:
    java web项目打包.war格式
    version 1.4.2-04 of the jvm is not suitable for thi
    Sugarcrm Email Integration
    sharepoint 2010 masterpage中必须的Content PlaceHolder
    微信开放平台
    Plan for caching and performance in SharePoint Server 2013
    使用自定义任务审批字段创建 SharePoint 顺序工作流
    Technical diagrams for SharePoint 2013
    To get TaskID's Integer ID value from the GUID in SharePoint workflow
    how to get sharepoint lookup value
  • 原文地址:https://www.cnblogs.com/sky-heaven/p/12562823.html
Copyright © 2011-2022 走看看