Kernel-5.10 内核休眠流程
用途: Linux的suspend机制是一种节能技术,用于将计算机的当前状态保存起来,然后进入休眠状态,以节省能源。在休眠状态下,计算机的硬件设备会停止工作,而保存的计算机状态被保存在内存或磁盘中。在用户空间向“/sys/power/state”文件分别写入“freeze”、“standby”和“mem”,即可触发它们。
1.内核休眠状态
1.1 电源管理休眠状态
电源管理休眠状态 | 说明 |
---|---|
freeze | 冻结I/O设备,将它们置于低功耗状态,使处理器进入空闲状态,唤醒最快,耗电比其它standby, mem, disk方式高 |
standby | 除了冻结I/O设备外,还会暂停系统,唤醒较快,耗电比其它 mem, disk方式高 |
mem | 将运行状态数据存到内存,并关闭外设,进入等待模式,唤醒较慢,耗电比disk方式高 |
disk | 将运行状态数据存到硬盘,然后关机,唤醒最慢 |
内核源码路径:msm-kernel/kernel/power/
查看电源管理状态路径:cat /sys/power/state
手动执行休眠:echo standby > /sys/power/state
1.2 内核休眠状态
状态 | ACPI等级 | 动作 | 进入方式 |
---|---|---|---|
Suspend-to-idle | S0 | 冻结用户空间程序 停止时间管理 IO设为低功耗状态 |
echo freeze > /sys/power/state 或 echo s2idle > /sys/power/mem_state echo mem > /sys/power/state |
Standby | S1 | 包含Suspend-to-idle动作 非引导cpu下线 |
echo standby > /sys/power/state 或 echo shallow > /sys/power/mem_state echo mem > /sys/power/state |
Suspend-to-RAM | S3 | 包含Standby所有动作 内存外所有设备进入低功耗状态 内存自动刷新 |
echo deep > /sys/power/mem_state echo mem > /sys/power/state |
Hibernation | S4 | 创建内存快照保存至disk 内核停止所有系统活动 唤醒所有设备进入低功耗状态 |
echo disk > /sys/power/state |
2 内核休眠流程
2.1 内核休眠流程图
内核路径:/msm-kernel/kernel/power/main.c
入口函数:state_store
2.2 电源管理休眠状态
路径:kernel/kernel5.4/include/linux/suspend.h
//表示系统处于完全开启状态,不进行任何挂起
#define PM_SUSPEND_ON ((__force suspend_state_t) 0)
//系统进入“空闲”模式,CPU 和设备可能处于低功耗状态,但系统仍然保持响应
#define PM_SUSPEND_TO_IDLE ((__force suspend_state_t) 1)
//系统处于待机状态,某些硬件组件可能已经进入低功耗模式,但系统仍然可以快速恢复
#define PM_SUSPEND_STANDBY ((__force suspend_state_t) 2)
//系统进入内存挂起状态(S3),系统状态保存在内存中允许快速恢复但消耗的电量较低
#define PM_SUSPEND_MEM ((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN PM_SUSPEND_TO_IDLE
#define PM_SUSPEND_MAX ((__force suspend_state_t) 4)
2.3 休眠入口 - state_store函数
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t n)
{
suspend_state_t state;
int error;
//获取电源自动休眠锁-保证state_store、mem_sleep_store、wakeup_count_store操作的完整性
error = pm_autosleep_lock();
if (error)
return error;
//如果已经是当前休眠状态的一种,就返回当前正忙
if (pm_autosleep_state() > PM_SUSPEND_ON) {
error = -EBUSY;
goto out;
}
//根据输入的信息,解析要休眠的状态
state = decode_state(buf, n);
if (state < PM_SUSPEND_MAX) {
if (state == PM_SUSPEND_MEM)
state = mem_sleep_current; //将当前休眠状态与内存休眠状态进行同步
error = pm_suspend(state); //执行休眠状态转换的操作
} else if (state == PM_SUSPEND_MAX) {
error = hibernate(); //执行系统休眠并保存镜像
} else {
error = -EINVAL;
}
out:
pm_autosleep_unlock();
return error ? error : n;
}
2.4 休眠执行函数 - pm_suspend
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);
2.5 休眠状态执行函数 - enter_state
执行进入系统睡眠状态所需的常见工作。
static int enter_state(suspend_state_t state)
{
int error;
//跟踪和记录系统的挂起(suspend)和恢复(resume)事件
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(); //更新 s2idle_state状态为S2IDLE_STATE_NONE
//同步开关:文件系统,同步更新数据到块设备 配置:sync_on_suspend文件节点
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_labels[state]);
//更新pm_suspend_global_flags标志位
pm_suspend_clear_flags();
error = suspend_prepare(state);//作用:做休眠前的准备工作
if (error)
goto Unlock;
if (suspend_test(TEST_FREEZER))
goto Finish;
trace_suspend_resume(TPS("suspend_enter"), state, false);
pm_pr_dbg("Suspending system (%s)\n", mem_sleep_labels[state]);
//限制内存分配
pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state); //进入设备休眠执行函数
//恢复内存分配
pm_restore_gfp_mask();
Finish:
events_check_enabled = false;
pm_pr_dbg("Finishing wakeup.\n");
//休眠完成
suspend_finish();
Unlock:
mutex_unlock(&system_transition_mutex);
return error;
}
2.6 休眠前准备函数 - suspend_prepare
准备进入系统睡眠状态
目标:为所有可以进入的系统睡眠状态(除休眠外)运行通用代码。运行挂起通知程序,分配“挂起”控制台并冻结进程。
static int suspend_prepare(suspend_state_t state)
{
int error;
if (!sleep_state_supported(state)) //判断状态是否为 PM_SUSPEND_TO_IDLE
return -EPERM;
//挂起控制台 - pm_vt_switch虚拟终端切换判断;
//vt_move_to_console(SUSPEND_CONSOLE, 1);将当前的前台控制台切换到指定的挂起控制台
//vt_kmsg_redirect(SUSPEND_CONSOLE);将内核消息重定向到挂起控制台
pm_prepare_console();
//通知部分设备做睡眠前的准备工作
error = pm_notifier_call_chain_robust(PM_SUSPEND_PREPARE, PM_POST_SUSPEND);
if