赞
踩
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/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()
入口: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个阶段:
主要是冻结应用程序和内核线程,外设进入睡眠状态。这个是公共部分,所有的suspend状态都要执行。
下图时一个基本的流程框架。
(suspend的流程框架图)
修改用户进程状态为 PF_FROZEN,使任务并进入__refrigerator( )里的一个循环,直到被唤醒——称之为 freeze。内核线程也是类似的情况,它进入另外的循环。freeze不是强制的,创建时可以配置为不可冻结。
冻结程序和内核线程的主要原因是:防止休眠时文件系统因为读写而损坏;防止驱动读写一个已经挂起的设备;等等(详见freezing-of-tasks.rst)。打开refrigerator( )里日志可以看到如下消息:
- Freezing user space processes ...
- systemd entered refrigerator
- gdbus entered refrigerator
- emacs entered refrigerator
- bash 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函数。
这个阶段主要时执行底层函数,进入待机状态,具体实现同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开始接管系统。
待机后,系统就挂在当前的执行位置,当用户唤醒(按电源键或者键盘)系统时,bios先恢复CPU,然后CPU从当前位置开始唤醒系统,唤醒刚好是一个相反的过程,先从架构相关的底层开始,逐级唤醒系统,主要的代码流程是在 suspend_enter( )的后半段,也是从suspend_ops->enter( ) 开始,这个函数的退出,表示系统已开始唤醒,接着syscore_resume( ) -->... --> ahci_pci_device_resume ( ) ....;依次调用各模块的 resume( ) callback。
入口是 hibernate( ) kernel/power/hibernate.c,主要的工作流程都在这个函数里面。
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_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
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。