Hibernate和Sleep功能介绍

Hibernate和Sleep两个功能是Linux Generic PM的核心功能, 他们的目的是类似的: 暂停使用–>保存上下文–>关闭系统以节电–>恢复系统–>恢复上下文–>继续使用

本文以向用户空间提供的接口为突破口,从整体上对这两个功能进行介绍,并会在后续的文章中,分析他们的实现逻辑和执行动作。

Hibernate和Sleep相关的术语推理
Hibernate(冬眠)和Sleep(睡眠)
是linux电源管理在用户角度的抽象,是用户可以看到的实实在在的东西。 它们的共同点,是保存系统运行的上下文后挂起系统,并在系统恢复后接着运行,就像什么都没发生一样。它们的不同点,是上下文保存的位置,系统恢复的触发方式以及具体的实现机制。

Suspend:
有两个层次的含义。 一是Hibernate和Sleep功能在底层实现上的统称, 都是指挂起系统,根据上下文的保存位置,可以分为suspend to disk(STD即hibernate, 上下文保存到硬盘/磁盘中)和Suspend to RAM(STR sleep的一种,上下文保存到RAM中); 二是Sleep功能在代码级的实现,表现为“kernel/power/suspend.c"文件

standy 是sleep功能的一个特例,可以翻译为“打盹”
正常的sleep,会在处理完上下文后,由arch-dependent代码将CPU置为低功耗状态(通常为Sleep)。 而现实中,根据对功耗和睡眠唤醒时间的不同需求,cpu可能会提供多种低功耗状态,如 除sleep之外,会提供standy状态,在该状态下,cpu处于浅睡模式,,有任何的风吹草动,都会立刻醒来。

wakeup:
我们多次提到恢复系统,在内核中就称为wakeup,hebernate时,会关闭整个系统的供电,因此想醒来只有power按钮可以用。而睡眠时,为了缩短wakeup时间,并不会关闭所有的供电,通常会保留重要设备的供电,比如键盘,那样这些设备就可以唤醒设备
这些刻意保留下来,可以唤醒系统的设备,统称为唤醒源(Wakeup source)。 而wakeup source的选择则是PM 设计工作(特别是Sleep, Standy等功能)的重点
软件架构及模块汇整:
软件架构

内核中该部分的软件架构大概可分为三个层次,如下图:

在这里插入图片描述

1)API Layer,描述用户空间API的一个抽象层

这里的API有两类,一类设计Hibernate和Sleep两个功能(global APIs), 包括实际功能,测试用功能,Debug用功能等, 通过sysfs和debugfs两种形式提供;另一类是Hibernate特有的(STD APIs), 通过sysfs和字符设备两种形式提供。

2)PM Core 电源管理的核心逻辑层,位于kernel/power目录下,包括主功能main, STD, STR&Standby以及辅助功能(assistant)等多个子模块。

主功能: 主要负责实现global APIs相关的逻辑,为用户空间提供相应的API

STD: 包括hibernate, snapshot, swap, block_io等子模块, 负责实现STD功能等硬件无关的逻辑。

STR &Standby: 包括suspend和suspend_test两个子模块,负责实现STR,Standby等功能和硬件无关的逻辑

3)PM Drvier: 电源管理驱动层,设计体系结构无关驱动,体系结构有关驱动,设备模型,以及各个设备驱动等多个软件模块。

用户空间接口
  • /sys/power/state

state是sysfs中一个文件,为 Generic PM核心接口, 在“kernel/power/main.c"中实现,用于将系统置于指定的Power state(供电模式,如Hibernate, sleep, standby). 不同的电源管理功能,在底层的实现,就是在不同的Power state之间切换。

获取该文件,返回当前系统支持的Power State,形式为字符串。 在内核中,有两种类型的Power State, 一种是Hibernate相关的,名称为”disk", 除“disk"之外,内核在”kernel/power/suspend.c”中通过数组的形式定义了两外三个state, 如下:

const char *pm_labels[] = { "mem", "standby", "freeze", NULL };
const char *pm_states[PM_SUSPEND_MAX];
pm_states[PM_SUSPEND_FREEZE] = pm_labels[relative_states ? 0 : 2];
这些Power state的解释如下:
 freeze:
 这种powerstate,并不涉及具体的Hardware或Driver, 只是冻结所有的进程,包括用户空间进程及内核线程。
 注: 之前的描述没有特别描述该state, 因为它在较早的内核中,只是Sleep, Hibernate等功能的一部分,只是最近才独立出来。另一个原因是,该state的省电效果不是很理想,所以其引用场景也是有限的。
 standby: 就是描述的standy
 mem: 就是通常所讲的sleep功能,suspend to ram
 disk: 即Hibernate,就是suspend to disk

写入特定的power state字符串,将会把系统置为该模式。

  • /sys/power/pm_trace

PM Trace用于提供电源管理过程中的Trace记录,由“CONFIG_PM_TRACE”宏定义(kernel/power/Kconfig)控制是否编译进内核,并由“/sys/power/pm_trace"文件在运行时控制是否使用该功能。

该功能的具体实现是”平台相关的,暂不描述

  • /sys/power/pm_test

PM_test 用于对电源管理功能的测试,由“CONFIG_PM_DEBUG”宏定义(kernel/power/Kconfig)控制是否编译进内核。

核心思想
1. 将电源管理过程按照先后顺序,划分为多个步骤, 如core, platform, devices等, 这些步骤称作PM Test Level.
2. 系统通过一个全局变量(pm_test_level), 保存系统当前的PM_Test_Level。 该变量的值可以通过“/sys/power/pm_test”文件获取及修改。
3. 在每一个电源管理步骤结束后, 插入PM test代码,改代码以当前执行步骤为参数,会判断当前的PM Test Level和执行步骤是否一致,如果一致,则说明该步骤执行成功。 出于Test考量, 执行成功后,系统会打印Test信息,并在等待一段时间后,推出PM过程。
4. 开发人员可以通过修改全局的Test Level, 有目的测试所关心的步骤是否执行成功。

上面已经讲了,该文件用于获取及修改PM Test Level, 具体的Level信息在“kernel/power/main.c”中定义, 格式如下:

static const char * const pm_tests[__TEST_AFTER_LAST] = {
	[TEST_NONE] = "none",
	[TEST_CORE] = "core",
	[TEST_CPUS] = "processors",
	[TEST_PLATFORM] = "platform",
	[TEST_DEVICES] = "devices",
	[TEST_FREEZER] = "freezer",
};
  • /sys/power/wakeup_count

该接口只和Sleep功能有关, 因此由“CONFIG_PM_SLEEP”宏定义(kernel/power/Kconfig)控制。 它的存在,是为了解决Sleep和Wakeup之间的同步问题。

我们知道,系统睡眠后,可以通过保留的wakeup Source唤醒系统。 而在当今的CPU体系中, 唤醒系统就是唤醒CPU, 而唤醒CPU的唯一途径,就是Wakeup Source产生中断(内核称作Wakeup event). 而内核要保证多种状态下,Sleep/Wakeup的行为都能正常。

1. 系统处于sleep状态时,产生了Wakeup event, 此时应该直接唤醒系统
2. 系统在进入sleep的过程中,产生了 wakeup event。此时应该放弃进入sleep
这一点就不容易做到了。 例如,当Wakeup event发生在“/sys/power/state"被写之后, 内核执行freeze操作之前。 此时用户空间程序一九可以处理wakup event, 或者只是部分处理 。而内核却以为该event已经被处理,因此并不会放弃此次sleep操作。

这就会造成,wakeup event发生后,用户程序已经后悔了,不想睡了,但最终还是睡下去了。 直到下一个wakeup event到来。

为了解决上述问题,内核提供wakeup_count机制,配合”/sys/power/state", 以实现Sleep过程中的同步。该机制的行为如下:

1. wakeup_count是内核用来保存当前wakeup event发生的计数
2. 用户空间程序在写入state 切换状态之前,应先读取wakeup_count并把获得的count写回给wakeup_count
3. 内核会对比写回的count和当前的count是否一致,如果不一致,就说明在读取和写回操作之间,产生了新的wakeup event, 内核就会返回错误。
4. 用户空间程序检测到写入错误之后,不能继续后面的动作,需要处理响应的event并伺机再次读取写回wakeup_count。
5. 如果内核对比一致, 会记录write  wakeup_count成功时的快照,后面继续suspend动作时,会检查是否和快照相符,如果不符,会终止suspend
6. 用户空间程序检测到写入正确后,可以继续对state的写入,以便发起一次状态切换。 而此时是安全的
后面会详细描述该机制在内核中的实现逻辑。
  • /sys/power/disk

该接口是STD特有的。用于设置或获取STD的类型。 当前内核支持的STD类型包括:

static const char * const hibernation_modes[] = {
	[HIBERNATION_PLATFORM]	= "platform",
	[HIBERNATION_SHUTDOWN]	= "shutdown",
	[HIBERNATION_REBOOT]	= "reboot",
#ifdef CONFIG_SUSPEND
	[HIBERNATION_SUSPEND]	= "suspend",
#endif
	[HIBERNATION_TEST_RESUME]	= "test_resume",
};
1. platform表示使用平台特有的机制,处理STD操作,如使用hibernation_ops等
2. shutdown, 通过关闭系统实现STD, 内核会调用kernel_power_off接口
3. reboot 通过重启系统实现STD, 内核会调用kernel_restart接口
【 以上两个kernel_xxx接口,参考Generic的reboot过程 】
4. suspend 利用STR功能,实现STD, 该类型下,STD和STR底层逻辑类似。
  • /sys/power/image_size

    该接口也是STD特有的。 STD的原理就是将当前运行的上下文保存到系统的disk(如NAND Flash, 如硬盘), 然后选择合适的方式关闭或重启系统。 保存上下文是需要存储空间的, 不光是disk的存储空间,也包括位于内存的用于交换或缓存的空间。

    而该接口,就是设置或者获取当前内存中需要分配多少空间,用于缓冲需要写入的disk数据。单位为byte。

  • /sys/power/reserverd_size

reserverd_size用于指示预留了多少内存空间,用于在->freeze()和->freeze_noirq过程中保存驱动分配的空间。 以免在STD的过程中丢失。

  • /sys/power/resume

该接口也是STD特有的。正常情况下,在重新开机后, 内核会在后期的初始化过程中,读取保存在disk的image, 并恢复系统,而该接口,提供了一种在用户空间的手动的读取image并恢复系统的方法。

通常情况下,该操作出现在系统 正常运行的过程中,需要加载并执行另外的image.

  • debugfs /suspend_status

此接口是以debugfs的形式,想用户空间提供suspend过程的统计信息,包括:成功的次数,失败的次数,freeze失败的次数等等

  • /dev/snapshot

该接口也是STD特有的。它通过字符设备的形式,向用户空间提供software的STD操作。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值