当前位置: 首页 > news >正文

linux的睡眠框架及实现

睡眠 4 种模式:

  • S2I (Suspend-to-Idle): 挂起系统,IO进入低功耗模式。需配置CONFIG_SUSPEND。

  • Standby:执行S2I后,把AP (nonboot CPU) 离线。除了CONFIG_SUSPEND的支持外,还需要向suspend子系统注册,如果是基于ACPI的系统,需要映射到S1状态。

  • S2RAM(Suspend-to-RAM):又称为STR,系统状态保存到内存,所有的外围设备,总线都进入低功耗或者断电的状态,内核在最后一步会把控制权给到BIOS,并映射到S3状态(ACPI系统)。除了CONFIG_SUSPEND的支持外,还需要向suspend子系统注册。

  • Hibernation(Suspend-to-Disk or STD) : 创建当前系统的内存镜像(内核和应用进程),保存到硬盘,然后,系统断电或者进入低功耗模式。唤醒时,bios启动一个新的内核(恢复内核)加载内存镜像,新内核自我更新,恢复先前的内核和进程状态。需要CONFIG_HIBERNATION的支持。

sys 接口

/sys/power/state (kernel/power/main.c)

  • state_show: 对应的read函数。

  • state_store:对应的write函数。

stat_show( )显示系统可用到睡眠模式,pm_states_init( ) 会初始化状态,其中S2I和 S2RAM是默认支持的,Standby 和 Hibernation 则需要进行检测硬件平台是否系统支持,不支持的话,就不会显示出来,显示值依次是:freeze,standby,mem,disk.

sys接口是功能的入口,从 stat_store( ) 里可以看到待机和休眠的入口函数分别是:

  • pm_suspend(state)

  • hibernate()

待机(SUSPEND)

入口:int pm_suspend(suspend_state_t state),有效参数是:

  • #define PM_SUSPEND_TO_IDLE ((__force suspend_state_t) 1)

  • #define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2)

  • #define PM_SUSPEND_MEM ((__force suspend_state_t) 3)

待机可以分为3个阶段:

1: 准备阶段

主要是冻结应用程序和内核线程,外设进入睡眠状态。这个是公共部分,所有的suspend状态都要执行。

下图时一个基本的流程框架。

(suspend的流程框架图)

冻结程序

修改用户进程状态为 PF_FROZEN,使任务并进入__refrigerator( )里的一个循环,直到被唤醒——称之为 freeze。内核线程也是类似的情况,它进入另外的循环。freeze不是强制的,创建时可以配置为不可冻结。

冻结程序和内核线程的主要原因是:防止休眠时文件系统因为读写而损坏;防止驱动读写一个已经挂起的设备;等等(详见freezing-of-tasks.rst)。打开refrigerator( )里日志可以看到如下消息:

    Freezing user space processes ... systemd entered refrigeratorgdbus entered refrigeratoremacs entered refrigeratorbash entered refrigerator

当前系统所有的应用程序都被冻结了,系统唤醒后,各任务退出循环,继续运行。

设备待机

主要是在DPM(device power manager,base/power/main.c)模块的 dpm_suspend( )中实现,轮询 dpm设备list,依次调用 device_suspend(dev) 来 callback每个设备注册的suspend( ). 以pci设备为例:

    struct bus_type pci_bus_typs = { .pm        = PCI_PM_OPS_PTR,}struct dev_pm_ops pci_dev_pm_ops = {.suspend = pci_pm_suspend, ...}

因此,每个pci设备都是先callback pci_pm_suspend( ),在这个函数里再callback各个设备的 pm->suspend(dev) 。

dpm list是通过 device_add( ) --> device_pm_add( ) 来生成的,也就是添加设备时,进行检测,支持PM管理的设备会被放入 dmp list,当执行suspend时,轮询该列表依次callback各个设备注册的 suspend函数。

2: 进入待机

这个阶段主要时执行底层函数,进入待机状态,具体实现同CPU架构相关。

入口是 suspend_enter( ),先检测pm状态,如果是S2I,则走s2idle_loop( ),不需要待机。否则走 syscore_suspend( ),先轮询syscore_ops_list,执行每个对象的suspend( ), 最后,调用平台相关的suspend_ops->enter(state),执行cpu模块的底层函数,写数据到bios后,系统由bios接管,进入待机状态。

syscore_ops_list 通过register_syscore_ops( )(drivers/base/syscore.c)来注册,一般在同cpu架构相关的代码里面。suspend_ops是通过suspend_set_ops( )来注册的,以龙芯mips为例:

suspend_set_ops(&loongson_pm_ops);
suspend_ops->enter -->loongson_pm_enter
-->mach_suspend: (arch/mips/loongson64/loongson-3/pm.c)-->保存一些寄存器
-->loongson_suspend_enter: (arch/mips/loongson64/loongson-3/sleep.S)写数据到bios后待机,系统在当前位置挂机,bios开始接管系统。

3: 恢复

待机后,系统就挂在当前的执行位置,当用户唤醒(按电源键或者键盘)系统时,bios先恢复CPU,然后CPU从当前位置开始唤醒系统,唤醒刚好是一个相反的过程,先从架构相关的底层开始,逐级唤醒系统,主要的代码流程是在 suspend_enter( )的后半段,也是从suspend_ops->enter( ) 开始,这个函数的退出,表示系统已开始唤醒,接着syscore_resume( ) -->... --> ahci_pci_device_resume ( ) ....;依次调用各模块的 resume( ) callback。

休眠(Hibernation)

入口是 hibernate( ) kernel/power/hibernate.c,主要的工作流程都在这个函数里面。

休眠的3种模式

cat /sys/power/disk 可以看到休眠支持的3种模式:

  • 'platform': 检测是否有平台支持(ACPI等),进入平台的待机模式。

  • 'shutdown':关机。

  • 'reboot': 重启,功能测试用。

工作流程

  • freeze_processes

  • freeze_kernel_threads()

  • dpm_suspend(PMSG_FREEZE)

  • create_image(platform_mode)

  • dpm_resume(msg)

  • swsusp_write(flags)

  • power_down( )

上面的函数流已经可以自解释了,先是冻结用户进程和内核线程,接着设备也进入待机,已防后面创建镜像时出现bug,镜像创建后,恢复设备,为关机做准备。最后的步骤是镜像写入交换分区,然后关机。

恢复的入口函数是:software_resume( ),加载内存镜像后,内核自我更新状态,恢复先前的状态。

ACPI

ACPI在睡眠框架里不是必选项,不是所有的架构都支持ACPI。它的入口是acpi_sleep_init( ) (drivers/acpi/sleep.c),主要是3个初始化函数:

  • acpi_sleep_syscore_init();

  • acpi_sleep_suspend_setup();

  • acpi_sleep_hibernate_setup();

分别注册对应级别的callback,当系统睡眠时,它会callback ACPI中相关的函数,与bios进行通讯,完成对应的睡眠功能。

参考资料

Documentation/admin-guide/pm/sleep-status.rst

Documentation/power/freezing-of-tasks.rst

http://www.lryc.cn/news/795.html

相关文章:

  • Java面试知识点
  • PTA Advanced 1159 Structure of a Binary Tree C++
  • CDN绕过技术总汇
  • 算法训练营DAY51|300.最长递增子序列、674. 最长连续递增序列、718. 最长重复子数组
  • mac:彻底解决-安装应用后提示:无法打开“XXX”,因为无法验证开发者的问题;无法验证此App不包含恶意软件
  • CPU 指标 user/idle/system 说明
  • Thinkphp大型进销存ERP源码/ 进销存APP源码/小程序ERP系统/含VUE源码支持二次开发
  • hgame2023 WebMisc
  • 67. Python的绝对路径
  • VHDL语言基础-组合逻辑电路-加法器
  • 内存检测工具Dr.Memory在Windows上的使用
  • J6412四网口迷你主机折腾虚拟机教程
  • 电子招标采购系统—企业战略布局下的采购寻源
  • elasticsearch 之 mapping 映射
  • 2023年rabbitMq面试题汇总2(5道)
  • 电视剧《狂飙》数据分析,正片有效播放市场占有率达65.7%
  • cas单点登录后重定向次数过多问题以及调试cas-dot-net-client
  • 【监控】Prometheus(普罗米修斯)监控概述
  • opencv+python物体检测【03-模仿学习】
  • 计算机科学基础知识第二节讲义
  • openssl genrsa 命令详解
  • C语言标准 —— C89(C90)、C99、C11、C17、C2X
  • 基于Java+Dubbo设计的智能公交查询系统
  • go语言的并发编程
  • 亚马逊要求UL94防火测试阻燃测试标准及项目
  • ClickHouse 合并树表引擎 MergeTree 原理分析
  • 用YOLOv8推荐的Roboflow工具来训练自己的数据集
  • 三层交换机【实验】
  • Anolis 8.6 部署 Kafka 3.3.1 安装和测试(二)
  • sed和awk