博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Android (Linux) Suspend流程
阅读量:4212 次
发布时间:2019-05-26

本文共 12699 字,大约阅读时间需要 42 分钟。

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]
  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.\n");  
  16. #endif   
  17.   
  18.     pr_debug("PM: Preparing system for %s sleep\n", 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\n", 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.\n");  
  34.     suspend_finish();  // 结束suspend,并被唤醒   
  35.  Unlock:  
  36.     mutex_unlock(&pm_mutex);  
  37.     return error;  
  38. }  
// kernel/kernel/power/suspend.cint enter_state(suspend_state_t state){	int error;	if (!valid_state(state))		return -ENODEV;	if (!mutex_trylock(&pm_mutex))		return -EBUSY;#ifdef CONFIG_SUSPEND_SYNC_WORKQUEUE	suspend_sys_sync_queue();#else	sys_sync();	printk("done.\n");#endif	pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);	error = suspend_prepare(); //准备进入suspend,并冻结所有进程	if (error)		goto Unlock;	if (suspend_test(TEST_FREEZER))		goto Finish;	pr_debug("PM: Entering %s sleep\n", pm_states[state]);	pm_restrict_gfp_mask();	error = suspend_devices_and_enter(state); // suspend外部设备	pm_restore_gfp_mask(); Finish:	pr_debug("PM: Finishing wakeup.\n");	suspend_finish();  // 结束suspend,并被唤醒 Unlock:	mutex_unlock(&pm_mutex);	return error;}

3.1 准备并冻结进程(suspend_prepare)

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

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

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

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

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

      其详细代码如下:

[cpp]
  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. }  
static int suspend_prepare(void){	int error;	if (!suspend_ops || !suspend_ops->enter)		return -EPERM;	pm_prepare_console();  // 分配一个console	error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); // 发送suspend notify	if (error)		goto Finish;	error = usermodehelper_disable(); // disable用户态的helper进程	if (error)		goto Finish;	error = suspend_freeze_processes(); // 冻结所有进程	if (!error)		return 0;	suspend_thaw_processes();	usermodehelper_enable(); Finish:	pm_notifier_call_chain(PM_POST_SUSPEND);	pm_restore_console();	return error;}

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]
  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\n");  
  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. }  
int suspend_devices_and_enter(suspend_state_t state){	int error;        /* suspend_pos通过suspend_set_ops来进行注册,           它在kernel/arch/arm/mach-xx/pm.c中定义,其函数名           可能为xx_pm_ops,例子如下:	   static struct platform_suspend_ops rk30_pm_ops = {		.enter		= xx_pm_enter,		.valid		= suspend_valid_only_mem,		.prepare 	= xx_pm_prepare,		.finish		= xx_pm_finish,	   };	*/	if (!suspend_ops)		return -ENOSYS;	trace_machine_suspend(state);	if (suspend_ops->begin) {		error = suspend_ops->begin(state);		if (error)			goto Close;	}	suspend_console(); // suspend console子系统,printk将不能打印了	suspend_test_start();	error = dpm_suspend_start(PMSG_SUSPEND);  // suspend所有非系统设备                                                  // 即执行所有设备的suspend回调函数	if (error) {		printk(KERN_ERR "PM: Some devices failed to suspend\n");		goto Recover_platform;	}	suspend_test_finish("suspend devices");	if (suspend_test(TEST_DEVICES))		goto Recover_platform;	error = suspend_enter(state); // 系统进入要求的sleep状态,                                      // 只有当wakeup时,此函数才返回 Resume_devices:	suspend_test_start();	dpm_resume_end(PMSG_RESUME);  // resume所有非系统设备                                      // 即执行所有设备的resume回调函数	suspend_test_finish("resume devices");	resume_console();             // resume console子系统,即printk可用了 Close:	if (suspend_ops->end)		suspend_ops->end();	trace_machine_suspend(PWR_EVENT_EXIT);	return error; Recover_platform:	if (suspend_ops->recover)		suspend_ops->recover();	goto Resume_devices;}

 

3.2.1 suspend_console

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

 

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

 

3.2.2 dpm_suspend_start  (PMSG_SUSPEND)

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

[cpp]
  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. }  
/** * dpm_suspend_start - Prepare devices for PM transition and suspend them. * @state: PM transition of the system being carried out. * * Prepare all non-sysdev devices for system PM transition and execute "suspend" * callbacks for them. */int dpm_suspend_start(pm_message_t state){	int error;	error = dpm_prepare(state);  // 根据dpm_list生成dpm_prepared_list	if (!error)		error = dpm_suspend(state); //根据dpm_prepared_list生成dpm_suspended_list	return error;}

  

3.2.3 suspend_enter 

  

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

[cpp]
  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\n");  
  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. }  
/** *	suspend_enter - enter the desired system sleep state. *	@state:		state to enter * *	This function should be called after devices have been suspended. */static int suspend_enter(suspend_state_t state){	int error;	if (suspend_ops->prepare) {		error = suspend_ops->prepare(); //即执行xx_pm_prepare		if (error)			goto Platform_finish;	}	error = dpm_suspend_noirq(PMSG_SUSPEND); //使所有外设驱动不再接收中断	if (error) {		printk(KERN_ERR "PM: Some devices failed to power down\n");		goto Platform_finish;	}	if (suspend_ops->prepare_late) {		error = suspend_ops->prepare_late();		if (error)			goto Platform_wake;	}	if (suspend_test(TEST_PLATFORM))		goto Platform_wake;	error = disable_nonboot_cpus(); // 停止非启动CPU	if (error || suspend_test(TEST_CPUS))		goto Enable_cpus;	arch_suspend_disable_irqs(); // 关闭中断	BUG_ON(!irqs_disabled());	error = syscore_suspend();  // 执行注册在syscore_ops_list的syscore_ops的suspend函数	if (!error) {		if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {			error = suspend_ops->enter(state); // KEY: 即执行xx_pm_enter,唤醒时才返回			events_check_enabled = false;		}		syscore_resume(); // 执行注册在syscore_ops_list的syscore_ops的resume函数	}	arch_suspend_enable_irqs(); // 打开中断	BUG_ON(irqs_disabled()); Enable_cpus:	enable_nonboot_cpus(); // 启动非启动CPU Platform_wake:	if (suspend_ops->wake)		suspend_ops->wake();	dpm_resume_noirq(PMSG_RESUME); //使所有外设驱动接收中断 Platform_finish:	if (suspend_ops->finish)		suspend_ops->finish(); //即执行xx_pm_finish	return error;}

  

3.2.4 dpm_resume_end  (PMSG_RESUME)

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

[cpp]
  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. }  
/** * dpm_resume_end - Execute "resume" callbacks and complete system transition. * @state: PM transition of the system being carried out. * * Execute "resume" callbacks for all devices and complete the PM transition of * the system. */void dpm_resume_end(pm_message_t state){	dpm_resume(state); //根据dpm_suspended_list生成dpm_prepared_list	dpm_complete(state); //根据dpm_prepared_list生成dpm_list}

 

3.2.5 resume_console

         resume console子系统,即printk可用了

[cpp]
  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. }  
void resume_console(void){	if (!console_suspend_enabled)		return;	down(&console_sem);	console_suspended = 0;	console_unlock();}

 

3.3 Suspend结束(suspend_finish)

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

      1) 解冻所有进程;

      2) 打开用户态helper进程;

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

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

     其详细代码如下:

 

[cpp]
  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. }  

转载地址:http://ehdmi.baihongyu.com/

你可能感兴趣的文章
matlab 实验
查看>>
使用conda安装包
查看>>
python 搭建web服务器
查看>>
python 获取wifi信息遇到的问题
查看>>
POJ 1321 搜索
查看>>
flask 接收wifi信息遇到的问题
查看>>
ubuntu 16.04 安装python虚拟环境产生的问题
查看>>
Linux screen简单用法
查看>>
蓝桥杯 决赛 2012_2 数据压缩
查看>>
scikit-learn 分类 KNeighborsClassifier
查看>>
支持浮点数的表达式求值
查看>>
基数排序
查看>>
蓝桥杯 2012 决赛 拼音字母
查看>>
Linux 虚拟环境找不到路径
查看>>
蓝桥杯 2012 C++B 决赛 方块填数
查看>>
C 字符串读入与取出空白符
查看>>
2013 蓝桥杯 C++ B决赛 高僧斗法(尼姆博弈)
查看>>
KMP实现
查看>>
蓝桥杯 2014 决赛 C++B组(4) 生物芯片(完全平方数)
查看>>
线段树 hdoj 1166 敌兵布阵
查看>>