Linux电源管理5(基于Linux6.6)---Generic PM Suspend介绍
一、概述
Linux内核提供了三种Suspend: Freeze、Standby和STR(Suspend to RAM),在用户空间向”/sys/power/state”文件分别写入”freeze”、”standby”和”mem”,即可触发它们。
在 Linux 内核中,Suspend 是一种电源管理机制,允许操作系统将系统的某些部分或整个系统置于低功耗状态,以延长电池寿命或减少能耗。Linux 提供了几种不同的挂起模式,常见的有 Freeze、Standby 和 STR (Suspend to RAM),它们分别有不同的行为和适用场景。
以下是这三种挂起模式的概述:
1.1、Freeze (冻结模式)
Freeze 是一种较轻的挂起模式,系统中的设备和进程会被冻结,但 CPU 和内存仍然处于活动状态。
-
工作原理:在 Freeze 模式下,操作系统会暂停所有进程的执行,尤其是用户空间的进程。内核会冻结所有与进程相关的活动,保持系统的基本状态不变。硬件设备可能会进入低功耗模式,但 CPU 会保持活动状态,内存内容保持不变。
-
使用场景:此模式通常用于需要快速恢复的场景,因为设备和内存状态都没有被完全关闭。设备的驱动程序可能会停止处理数据或任务,但内存中的数据保持完整,允许较短时间内恢复操作。
-
优点:恢复速度较快,因为系统状态没有被完全重置。
-
缺点:与其他更深层次的挂起模式相比,能效较低,因为 CPU 和内存仍在消耗电力。
1.2、Standby (待机模式)
Standby 模式是一个中间级别的挂起模式,系统的部分硬件可能会关闭,但 CPU 和内存依然保持活动状态。
-
工作原理:在 Standby 模式下,操作系统将尽量减少硬件的活动以节省电力。通常,待机模式下系统会关闭一些硬件设备,如硬盘、显示器等,但 CPU 和内存继续工作。类似于冻结模式,但是 Standby 模式下可能允许更多的硬件进入低功耗状态。
-
使用场景:适用于不需要完全关闭设备、但仍需要降低功耗的场景。Standby 模式常用于移动设备中,以便在短时间内快速恢复工作。
-
优点:比 Freeze 模式节省更多电力,恢复速度通常较快。
-
缺点:虽然比 Freeze 模式更节能,但恢复速度略慢,设备的一部分仍然处于活动状态。
1.3、STR (Suspend to RAM) - 睡眠模式
STR (Suspend to RAM),也称为 Suspend to RAM 或 睡眠模式,是一种深度挂起模式,系统会将大部分硬件和设备的电源关闭,同时将内存的内容保留在 RAM 中。
-
工作原理:在 STR 模式下,CPU 会完全停止工作,硬件大部分会进入休眠状态,只有内存仍然保持活动(即保持电力供应)。内存中保存着系统的当前状态,包括正在运行的进程和数据。此时,内存的内容被保留在 RAM 中,电力供应给内存,以确保系统可以从挂起状态中快速恢复。
-
使用场景:STR 模式适用于需要较长时间节省电力的情况,尤其是在笔记本电脑和移动设备上,当设备长时间不使用时,进入该模式以延长电池寿命。
-
优点:
- 最大限度减少电力消耗,因为大部分硬件被关闭。
- 恢复速度相对较快,因为系统的状态保存在内存中,恢复时无需重新启动。
-
缺点:
- 内存需要持续供电,否则数据会丢失。此模式对电池的依赖较大。
- 在某些硬件平台上,恢复过程可能不如预期顺利,存在硬件兼容性问题。
各模式的比较
模式 | 电源消耗 | 恢复速度 | 使用场景 | 特点 |
---|---|---|---|---|
Freeze | 较低 | 非常快 | 快速挂起并恢复,保持进程状态 | 冻结进程,保持内存,但不关闭硬件 |
Standby | 中等 | 快 | 短时间内降低功耗 | 部分硬件关闭,但 CPU 和内存继续活动 |
STR | 非常低 | 快 | 长时间节省电力,适合闲置时 | CPU 停止,内存内容保持,硬件大部分关闭 |
二、 suspend&resume过程概述
下面图片对Linux suspend&resume过程做了一个概述:
三、代码分析
3.1、suspend入口
在用户空间执行如下操作:
会通过sysfs触发suspend的执行,相应的处理代码如下:
static ssize_t state_store(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count)
{
const int online_type = mhp_online_type_from_str(buf);
struct memory_block *mem = to_memory_block(dev);
int ret;
if (online_type < 0)
return -EINVAL;
ret = lock_device_hotplug_sysfs();
if (ret)
return ret;
switch (online_type) {
case MMOP_ONLINE_KERNEL:
case MMOP_ONLINE_MOVABLE:
case MMOP_ONLINE:
/* mem->online_type is protected by device_hotplug_lock */
mem->online_type = online_type;
ret = device_online(&mem->dev);
break;
case MMOP_OFFLINE:
ret = device_offline(&mem->dev);
break;
default:
ret = -EINVAL; /* should never happen */
}
unlock_device_hotplug();
if (ret < 0)
return ret;
if (ret)
return -EINVAL;
return count;
}
power_attr定义了一个名称为state的attribute文件,该文件的store接口为state_store,该接口在lock住autosleep功能后,解析用户传入的buffer(freeze、standby or mem),转换成state参数。
state参数的类型为suspend_state_t,在include\linux\suspend.h中定义,为电源管理状态在内核中的表示。具体如下:
typedef int __bitwise suspend_state_t;
#define PM_SUSPEND_ON ((__force suspend_state_t) 0)
#define PM_SUSPEND_FREEZE ((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2)
#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN PM_SUSPEND_FREEZE
#define PM_SUSPEND_MAX ((__force suspend_state_t) 4)
根据state的值,如果不是(PM_SUSPEND_MAX,对应hibernate功能),则调用pm_suspend接口,进行后续的处理。
pm_suspend在kernel/power/suspend.c定义,处理所有的suspend过程。
3.2、pm_suspend & enter_state
pm_suspend的实现非常简单,简单的做一下参数合法性判断,直接调用enter_state接口,如下:
/**
* pm_suspend - Externally visible function for suspending the system.
* @state: System sleep state to enter.
*
* Check if the value of @state represents one of the supported states,
* execute enter_state() and update system suspend statistics.
*/
int pm_suspend(suspend_state_t state)
{
int error;
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
pr_info("suspend entry (%s)\n", mem_sleep_labels[state]);
error = enter_state(state);
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else {
suspend_stats.success++;
}
pr_info("suspend exit\n");
return error;
}
EXPORT_SYMBOL(pm_suspend);
enter_state代码为:
/**
* enter_state - Do common work needed to enter system sleep state.
* @state: System sleep state to enter.
*
* Make sure that no one else is trying to put the system into a sleep state.
* Fail if that's not the case. Otherwise, prepare for system suspend, make the
* system enter the given sleep state and clean up after wakeup.
*/
static int enter_state(suspend_state_t state)
{
int error;
trace_suspend_resume(TPS("suspend_enter"), state, true);
if (state == PM_SUSPEND_TO_IDLE) {
#ifdef CONFIG_PM_DEBUG
if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
pr_warn("Unsupported test mode for suspend to idle, please choose none/freezer/devices/platform.\n");
return -EAGAIN;
}
#endif
} else if (!valid_state(state)) {
return -EINVAL;
}
if (!mutex_trylock(&system_transition_mutex))
return -EBUSY;
if (state == PM_SUSPEND_TO_IDLE)
s2idle_begin();
if (sync_on_suspend_enabled) {
trace_suspend_resume(TPS("sync_filesystems"), 0, true);
ksys_sync_helper();
trace_suspend_resume(TPS("sync_filesystems"), 0, false);
}
pm_pr_dbg("Preparing system for sleep (%s)\n", mem_sleep_lab