Linux Suspend过程【转】
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代码如下:
- // kernel/kernel/power/suspend.c
- int 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()冻结所有的进程,这里将保存所有进程 当前的状态,也许有一些进程会拒绝进入冻结状态,当有这样的进程存在的时候,会导致冻结失败,此函数就会放弃冻结进程,并且解冻刚才冻结的所有进程。
其详细代码如下:
- 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
- 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将不能打印了
- 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回调函数
- /**
- * 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状态,然后停在这儿,只有当系统被中断或者其他事件唤醒时,此函数才返回,其详细代码如下:
- /**
- * 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回调函数
- /**
- * 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可用了
- 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) 释放分配的虚拟终端。
其详细代码如下:
- static void suspend_finish(void)
- {
- suspend_thaw_processes(); //解冻所有进程
- usermodehelper_enable(); // 打开用户态helper进程
- pm_notifier_call_chain(PM_POST_SUSPEND); // 广播系系统suspend结束的Notify
- pm_restore_console(); // 释放分配的虚拟终端
- }
Linux Suspend过程【转】的更多相关文章
- Android中Linux suspend/resume流程
Android中Linux suspend/resume流程首先我们从linux kernel 的suspend说起,不管你是使用echo mem > /sys/power/state 或者使用 ...
- 【原创】Linux Suspend流程分析
背景 Read the fucking source code! --By 鲁迅 A picture is worth a thousand words. --By 高尔基 说明: Kernel版本: ...
- Linux系统启动过程
1. 从BIOS到KERNEL BIOS自检->MBR(GRUB)->KERNEL->KERNEL自解压->内核初始化->内核启动 BIOS自检 当电脑开机的时候,电脑会 ...
- Linux启动过程详解(inittab、rc.sysinit、rcX.d、rc.local)
启动第一步--加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬 ...
- Linux启动过程详解
Linux启动过程详解 附上两张图,加深记忆 图1: 图2: 第一张图比较简洁明了,下面对第一张图的步骤进行详解: 加载BIOS 当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的 ...
- 理解Linux启动过程
传统的Linux系统启动过程主要由著名的init进程(也被称为SysV init启动系统)处理,而基于init的启动系统被认为有效率不足的问题,systemd是Linux系统机器的另一种启动方式,宣称 ...
- Linux 引导过程内幕
转载:http://www.ibm.com/developerworks/cn/linux/l-linuxboot/index.html 从主引导记录到第一个用户空间应用程序的指导 引导 Linu ...
- 嵌入式Linux启动过程中的问题积累
嵌入式Linux启动过程中的问题积累 Dongas 07-12-19 1.Bad Magic Number ## Booting image at 33000000 ... Bad Magic Num ...
- Linux系统启动过程介绍
Linux系统启动过程介绍 学习操作系统有必要了解一下系统的启动过程,这样在面对各种系统故障的时候能快速定位解决问题,下面以Centos来分析linux系统的启动过程. 1.BIOS自检:当开机的时候 ...
随机推荐
- [CodeForces954G]Castle Defense(二分答案+差分)
Description 题目链接 Solution 二分答案,套一个差分标记即可 每次放弓箭手显然越右边越优 Code #include <cstdio> #include <alg ...
- HTML5 canvas 圆盘抽奖
使用html5 canvas 绘制的圆盘抽奖程序 效果图: 贴上全部代码: 1 <!DOCTYPE html> <html> <head> <meta ch ...
- 通过aop添加日志管理
1.使用spring 的 aop 技术切到自定义注解上,所以先创建一个自定义注解类 import java.lang.annotation.*; @Target(ElementType.METHOD) ...
- Android的Fragment介绍
前言 fragment是从android3.0开始提出来的,用来支持大屏幕设备的ui设计.通过将activity划分为多个fragment,不仅提高了设计的灵活性,而且可以在程序运行时改变它们的特征, ...
- Div处理滚动条问题
1,用div做容器现在已经十分普遍,下面是最基本的代码 <div style="width:50px;height:50px;background-color:blue"&g ...
- Column 'sort' specified twice错误
我使用的是mybatis框架出现的这个问题,如果你们也出现了这个问题的豪华,我想你们的sql代码一定是复制的吧,额哈哈哈
- Python基础——安装运行
Python是如何运行的? 像绝大多数编程语言一样,要在计算机上能够运行python程序,至少需要安装一个最小的Python包:一个Python解释器和支持的库. 安装Python 安装包下载:htt ...
- Android如何实现毛玻璃效果之Android高级模糊技术
自从iOS系统引入了Blur效果,也就是所谓的毛玻璃.模糊化效果,磨砂效果,各大系统就开始竞相模仿,这是怎样的一个效果呢,我们先来看一下,如下面的图片: 效果我们知道了,如何在Android中实现呢, ...
- 移动APP自动化测试框架对比
转自微信公众号:腾讯移动品质中心TMQ 移动APP的UI自动化测试长久以来一直是一个难点,难点在于UI的”变”, 变化导致自动化用例的大量维护.从分层测试的角度,自动化测试应该逐层进行.最大量实现自动 ...
- mysql用root账户建立用户和赋予权限
1.创建用户 create user guest_test@localhost identified by "root";-- 创建名为guest_test的用户 2.赋予权限 - ...