Linux Suspend过程【转】

转自:http://blog.csdn.net/chen198746/article/details/15809363

目录(?)[-]

  1. Linux Suspend简介
  2. Suspend流程
  3. enter_statePM_SUSPEND_MEM
    1. 31 准备并冻结进程suspend_prepare
    2. 32 Suspend外部设备suspend_devices_and_enter
      1. 21 suspend_console
      2. 22 dpm_suspend_start  PMSG_SUSPEND
      3. 23 suspend_enter
      4. 24 dpm_resume_end  PMSG_RESUME
      5. 25 resume_console
    3. 3 Suspend结束suspend_finish

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. if (!mutex_trylock(&pm_mutex))
  8. return -EBUSY;
  9. #ifdef CONFIG_SUSPEND_SYNC_WORKQUEUE
  10. suspend_sys_sync_queue();
  11. #else
  12. sys_sync();
  13. printk("done.\n");
  14. #endif
  15. pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
  16. error = suspend_prepare(); //准备进入suspend,并冻结所有进程
  17. if (error)
  18. goto Unlock;
  19. if (suspend_test(TEST_FREEZER))
  20. goto Finish;
  21. pr_debug("PM: Entering %s sleep\n", pm_states[state]);
  22. pm_restrict_gfp_mask();
  23. error = suspend_devices_and_enter(state); // suspend外部设备
  24. pm_restore_gfp_mask();
  25. Finish:
  26. pr_debug("PM: Finishing wakeup.\n");
  27. suspend_finish();  // 结束suspend,并被唤醒
  28. Unlock:
  29. mutex_unlock(&pm_mutex);
  30. return error;
  31. }

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. if (!suspend_ops || !suspend_ops->enter)
  5. return -EPERM;
  6. pm_prepare_console();  // 分配一个console
  7. error = pm_notifier_call_chain(PM_SUSPEND_PREPARE); // 发送suspend notify
  8. if (error)
  9. goto Finish;
  10. error = usermodehelper_disable(); // disable用户态的helper进程
  11. if (error)
  12. goto Finish;
  13. error = suspend_freeze_processes(); // 冻结所有进程
  14. if (!error)
  15. return 0;
  16. suspend_thaw_processes();
  17. usermodehelper_enable();
  18. Finish:
  19. pm_notifier_call_chain(PM_POST_SUSPEND);
  20. pm_restore_console();
  21. return error;
  22. }

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. if (!suspend_ops)
  15. return -ENOSYS;
  16. trace_machine_suspend(state);
  17. if (suspend_ops->begin) {
  18. error = suspend_ops->begin(state);
  19. if (error)
  20. goto Close;
  21. }
  22. suspend_console(); // suspend console子系统,printk将不能打印了
  23. suspend_test_start();
  24. error = dpm_suspend_start(PMSG_SUSPEND);  // suspend所有非系统设备
  25. // 即执行所有设备的suspend回调函数
  26. if (error) {
  27. printk(KERN_ERR "PM: Some devices failed to suspend\n");
  28. goto Recover_platform;
  29. }
  30. suspend_test_finish("suspend devices");
  31. if (suspend_test(TEST_DEVICES))
  32. goto Recover_platform;
  33. error = suspend_enter(state); // 系统进入要求的sleep状态,
  34. // 只有当wakeup时,此函数才返回
  35. Resume_devices:
  36. suspend_test_start();
  37. dpm_resume_end(PMSG_RESUME);  // resume所有非系统设备
  38. // 即执行所有设备的resume回调函数
  39. suspend_test_finish("resume devices");
  40. resume_console();             // resume console子系统,即printk可用了
  41. Close:
  42. if (suspend_ops->end)
  43. suspend_ops->end();
  44. trace_machine_suspend(PWR_EVENT_EXIT);
  45. return error;
  46. Recover_platform:
  47. if (suspend_ops->recover)
  48. suspend_ops->recover();
  49. goto Resume_devices;
  50. }

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)\n");
  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. error = dpm_prepare(state);  // 根据dpm_list生成dpm_prepared_list
  12. if (!error)
  13. error = dpm_suspend(state); //根据dpm_prepared_list生成dpm_suspended_list
  14. return error;
  15. }

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. if (suspend_ops->prepare) {
  11. error = suspend_ops->prepare(); //即执行xx_pm_prepare
  12. if (error)
  13. goto Platform_finish;
  14. }
  15. error = dpm_suspend_noirq(PMSG_SUSPEND); //使所有外设驱动不再接收中断
  16. if (error) {
  17. printk(KERN_ERR "PM: Some devices failed to power down\n");
  18. goto Platform_finish;
  19. }
  20. if (suspend_ops->prepare_late) {
  21. error = suspend_ops->prepare_late();
  22. if (error)
  23. goto Platform_wake;
  24. }
  25. if (suspend_test(TEST_PLATFORM))
  26. goto Platform_wake;
  27. error = disable_nonboot_cpus(); // 停止非启动CPU
  28. if (error || suspend_test(TEST_CPUS))
  29. goto Enable_cpus;
  30. arch_suspend_disable_irqs(); // 关闭中断
  31. BUG_ON(!irqs_disabled());
  32. error = syscore_suspend();  // 执行注册在syscore_ops_list的syscore_ops的suspend函数
  33. if (!error) {
  34. if (!(suspend_test(TEST_CORE) || pm_wakeup_pending())) {
  35. error = suspend_ops->enter(state); // KEY: 即执行xx_pm_enter,唤醒时才返回
  36. events_check_enabled = false;
  37. }
  38. syscore_resume(); // 执行注册在syscore_ops_list的syscore_ops的resume函数
  39. }
  40. arch_suspend_enable_irqs(); // 打开中断
  41. BUG_ON(irqs_disabled());
  42. Enable_cpus:
  43. enable_nonboot_cpus(); // 启动非启动CPU
  44. Platform_wake:
  45. if (suspend_ops->wake)
  46. suspend_ops->wake();
  47. dpm_resume_noirq(PMSG_RESUME); //使所有外设驱动接收中断
  48. Platform_finish:
  49. if (suspend_ops->finish)
  50. suspend_ops->finish(); //即执行xx_pm_finish
  51. return error;
  52. }

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. }
时间: 2024-10-10 09:19:10

Linux Suspend过程【转】的相关文章

Linux启动过程笔记

Linux启动过程 1.启动流程(BIOS->MBR:Boot Code->引导GRUB->加载内核->执行init->runlevel) 2./boot/grub/下有多个文件   其中stage1为MBR镜像(512字节) stage2为引导程序 3./boot/grub/grub.conf为引导的配置文件 default=0#默认加载下边哪个系统 timeout=3#引导等待时间 splashimage=(hd0,1)/boot/grub/splash.xpm.gz#引

Linux系统启动过程

Linux启动过程 前言: Linux是一种自由和开放源代码的类UNIX操作系统.该操作系统的内核由林纳斯·托瓦兹在1991年10月5日首次发布.在加上用户空间的应用程序之后,成为Linux操作系统.Linux是自由软件和开放源代码软件发展中最著名的例子. 接触Linux的时间也不算短了,一直都是直接使用Linux操作系统进行一些工作,很少去了解系统从开机到能使用的整个过程,感觉有需要好好理解下整个系统的启动过程,故写这篇博客加深一下理解. 先通过一张图来简单了解下整个系统启动的流程,整个过程基

linux基础:5、linux启动过程

linux启动过程 1.BIOS(basic in out system) 通电检查硬件,然后加载第一可用启动项的mbr: #可通过BIOS设置各种介质存储设备的启动顺序,比如:cdrom.disk.usb 2.MBR(main boot recorder) 磁盘的第一个扇区,共512字节,446字节的boot程序空间,64字节的分区表,2字节的校验位. 执行MBR内保存的boot loader程序,一般为GRUB,通过GRUB来找到kernel和initrd(centos6里面此文件更名)并将

Linux启动过程

Linux启动过程 传说]散人--南宁1,开机,读取BIOS并进行自我检测2,透过BIOS取得第一个开机装置,读取MBR取得开机管理程序3透过开机管理程序,取得KERNEL(内核)加载内存且侦测系统硬件4核心计动呼叫INIT的程序5INIT程序开始执行系统初始化6依据INIT的程序进行daemon start7加载本机设定 22:19:25[传说]Ed1 2015/1/20 22:19:25 启动第一个程序init,读取配置文件fstab22:20:37[传说]散人--南 2015/1/20 2

Linux系统启动过程详解

 Linux系统启动过程详解 启动第一步--加载BIOS当你打开计算机电源,计算机会首先加载BIOS信息,BIOS信息是如此的重要,以至于计算机必须在最开始就找到它.这是因为BIOS中包含了CPU的相关信息.设备启动顺序信息.硬盘信息.内存信息.时钟信息.PnP特性等等.在此之后,计算机心里就有谱了,知道应该去读取哪个硬件设备了. 启动第二步--读取MBR众所周知,硬盘上第0磁道第一个扇区被称为MBR,也就是Master Boot Record,即主引导记录,它的大小是512字节,别看地方不大,

魏昊卿——《Linux内核分析》第三周作业:Linux系统启动过程

魏昊卿——<Linux内核分析>第三周作业:Linux系统启动过程 一.实验部分 实验指导 使用实验楼的虚拟机打开shell 1 cd LinuxKernel/ 2 qemu -kernel linux-3.18.6/arch/x86/boot/bzImage -initrd rootfs.img 内核启动完成后进入menu程序(<软件工程C编码实践篇>的课程项目),支持三个命令help.version和quit,您也可以添加更多的命令,对选修过<软件工程C编码实践篇>

Linux 引导过程内幕

http://www.ibm.com/developerworks/cn/linux/l-linuxboot/ 早期时,启动一台计算机意味着要给计算机喂一条包含引导程序的纸带,或者手工使用前端面板地址/数据/控制开关来加载引导程序.尽管目前的计算机已经装备了很多工具来简化引导过程,但是这一切并没有对整个过程进行必要的简化. 让我们先从高级的视角来查看 Linux 引导过程,这样就可以看到整个过程的全貌了.然后将回顾一下在各个步骤到底发生了什么.在整个过程中,参考一下内核源代码可以帮助我们更好地了

为什么要有uboot?带你全面分析嵌入式linux系统启动过程中uboot的作用

1.为什么要有uboot 1.1.计算机系统的主要部件 (1)计算机系统就是以CPU为核心来运行的系统.典型的计算机系统有:PC机(台式机+笔记本).嵌入式设备(手机.平板电脑.游戏机).单片机(家用电器像电饭锅.空调) (2)计算机系统的组成部件非常多,不同的计算机系统组成部件也不同.但是所有的计算机系统运行时需要的主要核心部件都是3个东西: CPU + 外部存储器(Flash/硬盘) + 内部存储器(DDR SDRAM/SDRAM/SRAM) 1.2.PC机的启动过程 (1)部署:典型的PC

linux启动过程-//-转

from http://opens.itpub.net/7668319/viewspace-856199/ linux系统的启动过程是由很多步骤组成的,但是,无论你是启动一个标准的x86桌面计算机,还是一个嵌入式PowerPC的目标板,大多数的流程是惊人得相似的.这篇文章,探索了linux从最初的启动准备到用户空间中某个程序被开启之间的启动过程,跟随这个流程,你还能学到其他许多与启动有关的知识,例如,boot loaders,内核解压缩,初始化内存盘,以及其他一些linux启动的部分.[@[em