Linux电源管理(6)_Generic PM之挂起功能
原文链接:Linux电源管理(6)_Generic PM之挂起功能
1.前言
Linux内核提供了三种暂停方式:Freeze,Standby和STR(暂停到RAM),在用户空间向” / sys / power / state”文件分别写入“ freeze”,“ standby”和“ mem”,可以触发。
另外内核中,挂起和恢复过程涉及到PM核心,设备PM,各个设备的驱动,平台相关的PM,CPU控制等多个模块,涉及控制台切换,进程冻结,CPU热插拔,唤醒处理等过个知识点。就让我们跟着内核代码,一一见识它们吧。
2.暂停功能有关的代码分布
内核中止功能相关的代码包括PM内核,设备PM,平台PM等几大块,具体如下:
1)PM核心
内核/电源/main.c----提供用户空间接口(/ sys /电源/状态)
kernel / power / suspend.c ----挂起功能的主逻辑
内核/电源/suspend_test.c----挂起功能的测试逻辑
内核/电源/控制台.c ----挂起过程中对控制台的处理逻辑
内核/电源/进程.c ----挂起过程中对进程的处理逻辑
2)平台相关的PM
include / linux / suspend.h ----定义平台相关的PM有关的操作函数集
arch / xxx / mach-xxx / xxx.c或者
arch / xxx / plat-xxx / xxx.c ----平台相关的电源管理操作
3.暂停和恢复过程概述
下面的图片对Linuxsuspend&resume过程由一个概述,读者可以顺着这个流程阅读内核源代码。具体的说明,可以参考后面的代码分析。
4.代码分析
4.1暂停入口
在用户空间执行如下操作:
回声“冻结”> / sys / power / state
回声“待机”> / sys / power / state
回声“内存”> / sys / power / state
会通过sysfs触发suspend的执行,相应的处理代码如下:
static ssize_t state_store(struct kobject *kobj,
struct kobj_attribute *attr,
const char *buf,
size_t n)
{
suspend_state_t state;
int error;
// 1. 获取自动睡眠锁
error = pm_autosleep_lock();
if (error)
return error;
// 2. 检查当前是否已处于非ON状态(禁止重复操作)
if (pm_autosleep_state() > PM_SUSPEND_ON) {
error = -EBUSY;
goto out;
}
// 3. 解析输入状态并执行对应操作
state = decode_state(buf, n);
if (state < PM_SUSPEND_MAX) {
error = pm_suspend(state); // 常规挂起
} else if (state == PM_SUSPEND_MAX) {
error = hibernate(); // 休眠到磁盘
} else {
error = -EINVAL; // 无效状态
}
out:
// 4. 释放锁并返回结果
pm_autosleep_unlock();
return error ? error : n;
}
power_attr (状态);
power_attr定义了一个名称为状态的属性文件,该文件的存储接口为state_store,该接口在锁定住自动休眠功能后,解析用户传入的缓冲器(冷冻,备用或MEM),转换成状态参数。
状态参数的类型为suspend_state_t,在包括\ linux \ suspend.h中定义,为电源管理状态在内核中的表示。具体如下:
typedef int __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)
根据状态的值,如果不是(PM_SUSPEND_MAX,对应于hibernate功能),则调用pm_suspend接口,进行后续的处理。
pm_suspend在内核/power/suspend.c定义,处理所有的suspend过程。
4.2 pm_suspend&enter_state
pm_suspend的实现非常简单,简单的做一下参数合法性判断,直接调用enter_state接口
int pm_suspend(suspend_state_t state)
{
int error;
// 1. 检查状态有效性
if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
return -EINVAL;
// 2. 进入挂起状态
error = enter_state(state);
// 3. 更新统计信息
if (error) {
suspend_stats.fail++;
dpm_save_failed_errno(error);
} else {
suspend_stats.success++;
}
return error;
}
enter_state代码为:
static int enter_state(suspend_state_t state)
{
int error;
// 1. 检查状态有效性
if (!valid_state(state))
return -ENODEV;
// 2. 获取互斥锁(非阻塞)
if (!mutex_trylock(&pm_mutex))
return -EBUSY;
// 3. 特殊处理FREEZE状态
if (state == PM_SUSPEND_FREEZE)
freeze_begin();
// 4. 同步文件系统
printk(KERN_INFO "PM: Syncing filesystems... ");
sys_sync();
printk("done.\n");
// 5. 系统挂起准备
pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
error = suspend_prepare(state);
if (error)
goto Unlock;
// 6. 测试冻结器(可选调试)
if (suspend_test(TEST_FREEZER))
goto Finish;
// 7. 核心挂起流程
pr_debug("PM: Entering %s sleep\n", pm_states[state]);
pm_restrict_gfp_mask();
error = suspend_devices_and_enter(state);
pm_restore_gfp_mask();
Finish:
// 8. 唤醒后处理
pr_debug("PM: Finishing wakeup.\n");
suspend_finish();
Unlock:
// 9. 释放锁并返回
mutex_unlock(&pm_mutex);
return error;
}
主要工作包括:
a)调用valid_state,判断该平台是否支持该电源状态。
暂停的最终目的,是让系统进入可恢复的挂起状态,而该功能必须有平台相关代码的参与才能完成,因此内核PM Core就提供了一系列的替代函数(封装在platform_suspend_ops中),让平台代码(如arch / arm / mach-xxx / pm.c)实现,然后由PM Core在适当的时机调用。这些变量函数包含一个有效函数,就是可以识别PM Core,支持某种状态。
最后看一下valid_state的实现(删除了无关代码):
bool valid_state(suspend_state_t state)
{
// 1. FREEZE 状态始终有效
if (state == PM_SUSPEND_FREEZE) {
return true;
}
// 2. 其他状态需要底层支持
/*
* PM_SUSPEND_STANDBY 和 PM_SUSPEND_MEM 需要:
* - suspend_ops 存在
* - 且实现了 valid 回调
* - 且回调返回 true
*/
return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}
如果是冻结,无需平台代码参与即可支持,直接返回true。对于standby和mem,则需要调用suspend_ops的有效回送,由替代平台代码判断是否支持。
b)加互斥锁,只允许一个实例处理暂停。
c)如果状态为冻结,则调用freeze_begin,进行暂停以冻结相关的特殊动作。我会在后面统一分析freeze的特殊动作,此处暂不描述。
d)打印提示信息,同步文件系统。
e)调用suspend_prepare,进行suspend前的准备,主要包括切换控制台和进程与线程冻结。如果失败,则终止suspend过程。
f)然后,调用suspend_devices_and_enter接口,该接口负责suspend和resume的所有实际动作。前半部分,suspend console,suspend device,关中断,调用平台相关的suspend_ops使系统进入低流量状态。后半部分,在系统被事件唤醒后,处理相关动作,调用平台相关的suspend_ops恢复系统,开中断,恢复设备,恢复控制台。
g)最后,调用suspend_finish,恢复(或等待恢复)process&thread,还原控制台。
4.3 suspend_prepare
suspend_prepare的代码如下:
static int suspend_prepare(suspend_state_t state)
{
int error;
// 1. 检查挂起操作是否可用
if (need_suspend_ops(state) && (!suspend_ops || !suspend_ops->enter))
return -EPERM;
// 2. 准备控制台
pm_prepare_console();
// 3. 调用挂准备通知链
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
// 4. 冻结用户进程
error = suspend_freeze_processes();
if (!error)
return 0;
// 5. 冻结失败处理
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
Finish:
// 6. 清理工作
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
主要工作为:
a)检查suspend_ops是否提供了。enter替代,没有的话,返回错误。
b)调用pm_prepare_console,将当前console切换到一个虚拟控制台并重新连接内核的kmsg(需要的话)。该功能切换VT开关,后面我会在稍微详细的介绍一下,但Linux控制台是相当复杂的,更具体的分析,要在控制台上方的分析文章中说明。
c)调用pm_notifier_call_chain,发送暂停开始的消息(PM_SUSPEND_PREPARE),后面会详细描述。
d)调用suspend_freeze_processes,冻结用户空间进程和一些内核线程。该功能变量冻结任务,我会专门用一篇文章去分析它。此处不再详细说明了。
e)如果任务冻结失败,调用pm_restore_console,将console切换回原来的console,并返回错误,刹车能终止挂起。
4.4suspend_devices_and_enter
suspend_devices_and_enter的过程较多复杂,代码实现如下:
int suspend_devices_and_enter(suspend_state_t state)
{
int error;
bool wakeup = false;
// 1. 检查挂起操作是否可用
if (need_suspend_ops(state) && !suspend_ops)
return -ENOSYS;
trace_machine_suspend(state);
// 2. 调用平台特定的挂起开始函数
if (need_suspend_ops(state) && suspend_ops->begin) {
error = suspend_ops->begin(state);
if (error)
goto Close;
}
// 3. 准备系统环境
suspend_console();
ftrace_stop();
suspend_test_start();
// 4. 挂起设备
error = dpm_suspend_start(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to suspend\n");
goto Recover_platform;
}
suspend_test_finish("suspend devices");
// 5. 设备挂起测试
if (suspend_test(TEST_DEVICES))
goto Recover_platform;
// 6. 进入挂起状态(可能循环)
do {
error = suspend_enter(state, &wakeup);
} while (!error && !wakeup && need_suspend_ops(state) &&
suspend_ops->suspend_again && suspend_ops->suspend_again());
Resume_devices:
// 7. 恢复设备
suspend_test_start();
dpm_resume_end(PMSG_RESUME);
suspend_test_finish("resume devices");
ftrace_start();
resume_console();
Close:
// 8. 调用平台特定的挂起结束函数
if (need_suspend_ops(state) && suspend_ops->end)
suspend_ops->end();
trace_machine_suspend(PWR_EVENT_EXIT);
return error;
Recover_platform:
// 9. 挂起失败时的平台恢复
if (need_suspend_ops(state) && suspend_ops->recover)
suspend_ops->recover();
goto Resume_devices;
}
a)再次检查平台代码是否需要提供以及是否提供了suspend_ops。
b)调用suspend_ops的开始(有的话),通知平台代码,刹车让其作相应的处理(需要的话)。可能失败,需要跳至Close处执行恢复操作(suspend_ops-> end)。
该接口由“ kernel \ printk.c”实现,主要是保持住一个锁,该锁会阻止其他代码访问控制台。
d)调用ftrace_stop,停止ftrace功能。ftrace是一个很有意思的功能,后面再介绍。
e)调用dpm_suspend_start,调用所有设备的-> prepare和-> suspend变量函数(具体可参考“ Linux电源管理(4)_Power Management Interface ”的描述),suspend需要正常suspend的设备。suspend设备可能失败,需要跳至Recover_platform,执行recover操作(suspend_ops-> recover)。
f)以上都是suspend前的准备工作,此时,调用suspend_enter接口,使系统进入指定的电源状态。该接口的内容如下:
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
int error;
// 1. 平台早期准备
if (need_suspend_ops(state) && suspend_ops->prepare) {
error = suspend_ops->prepare();
if (error)
goto Platform_finish;
}
// 2. 设备挂起最终阶段
error = dpm_suspend_end(PMSG_SUSPEND);
if (error) {
printk(KERN_ERR "PM: Some devices failed to power down\n");
goto Platform_finish;
}
// 3. 平台晚期准备
if (need_suspend_ops(state) && suspend_ops->prepare_late) {
error = suspend_ops->prepare_late();
if (error)
goto Platform_wake;
}
// 4. 平台测试(调试用)
if (suspend_test(TEST_PLATFORM))
goto Platform_wake;
// 5. 处理FREEZE状态
/*
* PM_SUSPEND_FREEZE 等同于:
* 冻结进程 + 挂起设备 + 空闲处理器
* 所以应在所有设备挂起后立即调用
*/
if (state == PM_SUSPEND_FREEZE) {
freeze_enter();
goto Platform_wake;
}
// 6. 禁用非启动CPU
error = disable_nonboot_cpus();
if (error || suspend_test(TEST_CPUS))
goto Enable_cpus;
// 7. 核心挂起流程
arch_suspend_disable_irqs();
BUG_ON(!irqs_disabled());
error = syscore_suspend();
if (!error) {
*wakeup = pm_wakeup_pending();
if (!(suspend_test(TEST_CORE) || *wakeup)) {
error = suspend_ops->enter(state);
events_check_enabled = false;
}
syscore_resume();
}
arch_suspend_enable_irqs();
BUG_ON(irqs_disabled());
Enable_cpus:
// 8. 重新启用非启动CPU
enable_nonboot_cpus();
Platform_wake:
// 9. 平台唤醒通知
if (need_suspend_ops(state) && suspend_ops->wake)
suspend_ops->wake();
dpm_resume_start(PMSG_RESUME);
Platform_finish:
// 10. 平台收尾工作
if (need_suspend_ops(state) && suspend_ops->finish)
suspend_ops->finish();
return error;
}
f1)该接口处理完后,会通过返回值确认是否enter成功,同时通过唤醒指针,通知调用者,是否有唤醒事件发生,导致电源状态切换失败。
f2)调用suspend_ops的prepare准备(有的话),通知平台代码,杀死让其在即将进行状态切换之时,再做一些处理(需要的话)。该可能失败(平台代码出现意外),失败的话,需要跳至Platform_finish处,调用suspend_ops的finish替代,执行恢复操作。
f3)调用dpm_suspend_end,调用所有设备的-> suspend_late和-> suspend_noirq变量函数(具体可参考“ Linux电源管理(4)_Power Management Interface ”的描述),暂停后期悬浮设备和需要在关闭中断下的suspend的设备。需要说明的是,这里的noirq,是通过禁止所有的中断线的形式,而不是通过关中断的方式。同样,该操作可能会失败,失败的话,跳到Platform_finish处,执行恢复动作。
f4)调用suspend_ops的prepare_late替换(有的话),通知平台代码,杀死让其在最后关头,再做一些处理(需要的话)。该可能失败(平台代码出现意外),失败的话,需要跳至Platform_wake处,调用suspend_ops的唤醒,执行设备的恢复,调用suspend_ops的完成,执行恢复操作。
f5)如果是暂停冻结,执行相应的操作,包括冻结进程,已暂停的设备(参数为PM_SUSPEND_FREEZE),cpu进入空闲状态。如果有任何事件使CPU从空闲状态退出,跳至平台_唤醒位置,执行唤醒操作。
f6)调用disable_nonboot_cpus,禁止所有的非boot cpu。也会失败,执行恢复操作即可。
f7)调用arch_suspend_disable_irqs,关闭中断。如果无法关闭,则为bug。
f8)调用syscore_suspend,挂起系统核心。同样会失败,执行恢复操作即可。有关syscore,我会在另一篇文章中详细描述。
f9)如果很幸运,以上操作都成功了,那么,切换吧。不过,别高兴太早,还得调用pm_wakeup_pending检查一下,这段时间,是否有唤醒事件发生,如果有就要终止的暂停。
f10)如果一切顺利,调用suspend_ops的enter变量,进行状态切换。这时,系统应该已经suspend了……
f11)suspend过程中,唤醒事件发生,系统唤醒,该函数接着执行resume动作,并最终返回。resume动作基本上是suspend的反动作,就不再继续分析了。
f12)或者,由于意外,暂停终止,该函数也会返回。
g)suspend_enter返回,如果返回原因不是发生错误,而且不是唤醒事件。则调用suspend_ops的suspend_again可以,检查是否需要再次暂停。再什么情况下要再次suspend呢?需要看具体的平台了,谁知道呢。
h)继续恢复操作,恢复设备,启动ftrace,恢复控制台,suspend_ops-> end等。
i)该函数返回后,表示系统已经恢复。
4.5suspend_finish
比较简单:
static void suspend_finish(void)
{
// 1. 解冻被挂起的进程
suspend_thaw_processes();
// 2. 发送挂起结束通知
pm_notifier_call_chain(PM_POST_SUSPEND);
// 3. 恢复控制台
pm_restore_console();
}
a)恢复所有的用户空间进程和内核线程。
b)发送暂停结束的通知。
c)将console切换回原来的。
5.重要知识点回顾
5.1 VT开关
通常情况下,系统控制台模块(driver \ tty \ vt \)会在暂停的过程中,重新分配一个控制台,将控制台切换到该控制台上。然后在恢复时,切换回旧的控制台。就是VT switch功能。VT开关是很耗时的,因此内核提供了一些机制,控制是否使用这个功能:
1)提供一个接口函数pm_set_vt_switch(drivers \ tty \ vt \ vt_ioctl.c),方便其他内核模块从整体上关闭或开启VT switch功能。
2)VT开关VT开关处于开启状态时,满足以下的一种条件(可参考kernel \ power \ console.c相关的描述),即称为能VT开关
一)有控制台驱动程序调用pm_vt_switch_required接口,显式的要求使能VT开关。PM核心的控制台模块会把这些信息记录在一个名称为pm_vt_switch_list的链表中。
b)系统禁止在suspend的过程中暂停控制台(由kernel / printk.c中的console_suspend_enabled变量控制)。很有可能需要使用console查看suspend过程,此时为了使console不混乱,有必要进行VT switch。
c)没有任何控制台驾驶员关心是否需要VT开关,换句话说没有任何驾驶员调用pm_vt_switch_required接口要求使能或禁止VT开关功能。此时会遵循旧的习惯,进行VT开关。
因此,悬浮过程对console的处理分为4步:
准备控制台:负责在需要VT swich时,将当前控制台切换到SUSPEND控制台。
int pm_prepare_console(void)
{
// 1. 检查是否需要虚拟终端切换
if (!pm_vt_switch())
return 0;
// 2. 切换到挂起控制台
orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
if (orig_fgconsole < 0)
return 1;
// 3. 重定向内核消息
orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
return 0;
}
悬浮式控制台:挂起控制台,由内核/printk.c实现,主要是保持住控制台用的互斥锁,使他人无法使用控制台。
简历控制台:对控制台解锁。
restore console:将console恢复为初始的console。
void pm_restore_console(void)
{
// 1. 检查是否需要虚拟终端切换
if (!pm_vt_switch())
return;
// 2. 恢复原始控制台设置
if (orig_fgconsole >= 0) {
vt_move_to_console(orig_fgconsole, 0);
vt_kmsg_redirect(orig_kmsg);
}
}
5.2冻结任务
进度的冻结功能,是暂停,休眠,等电源管理功能的组成部分,在新版本内核中,它被独立出来,作为一个独立的电源管理状态(冻结)。该功能的目的,是在电源管理的状态切换过程中,确保所有用户空间进程和部分内核线程处于一个稳定的状态。
5.3 PM通知者
PM notifier是基于内核的blocking notifier功能实现的。。block notifier提供了一种内核内部的消息通知机制,消息接受者通过notifier注册的方式,注册一个较小的函数,关注消息发送者发出的notifier。当消息产生时,,消息产生者通过调用称为函数的形式,通知消息接受者。这种调用,是可以被分割的,因此阻止了通知程序。
那suspend功能为什么使用notifier呢?原因可能有多个,这里我举一个例子,这是我们日常开发中可能会遇到的。
由之前的描述可知,暂停过程中,暂停设备发生在进程被冻结之后,恢复设备发生在进程被恢复之前。
1)如果有些设备就需要在冻结进度之前暂停如何办?
2)如果某些设备的恢复动作需要连续重复,或者要等待什么事情发生,那么如果它的恢复动作发生在进程恢复之前,岂不是要阻止所有进程的恢复?更甚者,如果该设备要等待某个进程的数据才能恢复,怎么办?
再来看suspend_prepare和suspend_finish中的处理:
static int suspend_prepare(suspend_state_t state)
{
int error;
// 1. 调用挂起准备通知链
error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);
if (error)
goto Finish;
// 2. 冻结用户进程
error = suspend_freeze_processes();
if (!error)
return 0;
// 3. 错误处理
suspend_stats.failed_freeze++;
dpm_save_failed_step(SUSPEND_FREEZE);
Finish:
// 4. 清理工作
pm_notifier_call_chain(PM_POST_SUSPEND);
pm_restore_console();
return error;
}
static void suspend_finish(void)
{
// 1. 解冻用户进程
suspend_thaw_processes();
// 2. 调用挂起完成通知
pm_notifier_call_chain(PM_POST_SUSPEND);
// 3. 恢复控制台
pm_restore_console();
}
原来PM notifier是在设备模型的框架外,开了一个后门,那些比较特殊的驱动程序,可以绕过设备模型,直接接收PM发送的暂停信息,杀死执行自身的暂停动作。特别是resume时,可以在其他进展都正好工作的时候,只让暂停进展
感兴趣的读者,可以围观一下下面这个活生生的例子(顺便提一下,好的设计是不应该有例外的):
驱动程序\视频\ omap2 \ dss \ core.c
5.4设备PM ops和平台PM ops的调用时机
对Linux的驱动工程师来说,设备PM OPS和平台PM OPS就是电源管理(暂停)的全部,只要在合适的地方,实现合适的回调函数,即可实现系统的电源管理。但现实太复杂了,以如kernel提供的这两个数据结构也很复杂,再回忆一下,如下:
struct dev_pm_ops {
/* 基础电源管理回调 */
int (*prepare)(struct device *dev);
void (*complete)(struct device *dev);
int (*suspend)(struct device *dev);
int (*resume)(struct device *dev);
int (*freeze)(struct device *dev);
int (*thaw)(struct device *dev);
int (*poweroff)(struct device *dev);
int (*restore)(struct device *dev);
/* 带时序后缀的回调 */
int (*suspend_late)(struct device *dev);
int (*resume_early)(struct device *dev);
int (*freeze_late)(struct device *dev);
int (*thaw_early)(struct device *dev);
int (*poweroff_late)(struct device *dev);
int (*restore_early)(struct device *dev);
/* noirq(无中断上下文)回调 */
int (*suspend_noirq)(struct device *dev);
int (*resume_noirq)(struct device *dev);
int (*freeze_noirq)(struct device *dev);
int (*thaw_noirq)(struct device *dev);
int (*poweroff_noirq)(struct device *dev);
int (*restore_noirq)(struct device *dev);
/* 运行时电源管理 */
int (*runtime_suspend)(struct device *dev);
int (*runtime_resume)(struct device *dev);
int (*runtime_idle)(struct device *dev);
};
struct platform_suspend_ops {
/* 状态验证 */
int (*valid)(suspend_state_t state);
/* 挂起流程控制 */
int (*begin)(suspend_state_t state);
int (*prepare)(void);
int (*prepare_late)(void);
int (*enter)(suspend_state_t state);
/* 唤醒流程控制 */
void (*wake)(void);
void (*finish)(void);
/* 高级控制 */
bool (*suspend_again)(void);
void (*end)(void);
void (*recover)(void);
};
虽然内核的注释已经相当详细了,但我们一定会犯晕,到底该实现什么地方?这些替代的应用场景又是什么?蜗蜗以为,要熟练使用这些替代,唯一的方法就是多编码,多理解。,另外,我们可以总结一下在电源状态切换时,这些替代的调用时机,从侧面帮助理解。如下(只介绍和suspend功能有关的,struct dev_pm_ops简称D,struct platform_suspend_ops简称P):
5.5暂停过程的同步和PM唤醒
最重要的事情,如果暂停的过程中,有唤醒事件产生怎么办?正常的流程,应该终止暂停,返回并处理事件。但由于暂停而发生的特殊性,进程被冻结,关断等等,导致事情并没有那么简单,以至于在很久的一段时间内,内核都不能很好的处理。这也称作挂起过程的同步问题。
在美好的旧时光里,suspend主要是由于热关机,因此同步问题的影响并不突出(因为操作并不重复)。但来到新时代之后,事情变了,Android竟然用suspend作日常的暂停(那怎么解决呢?得靠系统唤醒框架,也就是suspend过程中所调用的pm_wakeup_pending接口所在的模块。我会在下一篇文章中继续该模块的分析,这里就不再继续了。
相关文章:
Linux电源管理(6)_Generic PM之挂起功能
原文链接:Linux电源管理(6)_Generic PM之挂起功能 1.前言 Linux内核提供了三种暂停方式:Freeze,Standby和STR(暂停到RAM),在用户空间向” / sys / power / state”文件分别写入“ …...
MCP原理详解及实战案例(动嘴出UI稿、3D建模)
文章目录 MCP 原理介绍架构核心组件协议层传输层连接生命周期MCP与function calling: 互补关系 MCP python SDKMCP的优点 怎么用MCP:天气服务参考应用项目: REF 24年11月份,claude推出了模型上下文协议( MCP),作为一种潜在的解决方案…...
【Java项目脚手架系列】第二篇:JavaWeb项目脚手架
【Java项目脚手架系列】第二篇:JavaWeb项目脚手架 前言 在Java Web开发中,一个好的项目脚手架可以大大提高开发效率,减少重复工作。本篇文章将介绍一个基于Maven的JavaWeb项目脚手架,它包含了基础的Web开发配置和常用功能。 什…...
【机器学习-线性回归-5】多元线性回归:概念、原理与实现详解
线性回归是机器学习中最基础且广泛应用的算法之一,而多元线性回归则是其重要扩展。本文将全面介绍多元线性回归的核心概念、数学原理及多种实现方式,帮助读者深入理解这一强大的预测工具。 1. 多元线性回归概述 1.1 什么是多元线性回归 多元线性回归(…...
《TCP/IP详解 卷1:协议》之第十章:动态选路协议
目录 一、常见的动态路由协议 二、RIP 1、RIP 版本1: 1.1、报文格式 2、RIP 版本2: 2.1、报文格式 三、OSPF 1、链路状态路由协议 2、工作原理 3、OSPF的特点 四、BGP 五、参考链接 一、常见的动态路由协议 路由协议(Routing Pr…...
逆向常见题目—迷宫类题目
逆向常见题目—迷宫类题目 迷宫(maze) 思路: 1.找到地图(字符串) 2.找到方向(上左下右) 3.找到起点到终点 然后将路径输出即可 特征: 标题,hint为maze 或者 看到字符串###等等 整理字符串为图形.py (要是不是正方形需要自己输出行和列) import mathdef arra…...
Redis 数据类型详解(二):Hash 类型全解析
文章目录 一、什么是 Redis 的 Hash 类型?二、Hash为什么在有些时候比String好用三、常见命令1.HSET key field value2.HGET key field3.HMSET4.HMGET5.HGETALL6.HKEYS7.HVALS8.HINCRBY9.HSETNX 四、应用场景五、性能优势六、注意事项总结 提示:以下是本…...
Vite简单介绍
Vite 是一个现代化的前端构建工具,由 Vue.js 的创始人 Evan You 开发,旨在提供更快的开发体验和更高效的构建流程。它的名字来源于法语单词“vite”,意为“快速”,这也反映了它的核心优势——极速的冷启动和热模块替换(…...
jwt身份验证和基本的利用方式
前言 : 什么是jwt(json web token)? 看看英文单词的意思就是 json形式的token 他的基本的特征 : 类似于这样的 他有2个点 分割 解码的时候会有三个部分 头部 payload 对称密钥 这个就是对称加密 头部&am…...
基于YOLOv的目标检测训练数据构建方法研究—图像采集、标注、划分与增强一体化流程设计
在目标检测任务中,高质量的训练数据是模型性能提升的关键。本文围绕 YOLOv 系列模型,系统性地研究了目标检测训练数据的构建方法,提出了一套从图像采集、标注、数据集划分到数据增强的一体化流程设计 。通过多源图像采集策略确保样本多样性&a…...
c++类【开端】
运算符重载 方式:operator运算符(参数列表){}。 运算符就是:-*/[]等。 运算符重载,和定义一个方法效果是一样的,只是,重载运算符让类的-*/等操作看起来和普通数字的-*/一样。仅是看起来一样。我们重载运算符…...
wordperss AI插件:AI图文+视频+长尾关键词自动生成,已内置deepseek、kimi全模型,支持简单一键接入更多自定义API
【2.17最新版】Linkreate wordperss AI插件:AI图文视频长尾关键词自动生成,已内置deepseek、kimi全模型。 支持自定义接入其它API,包括但不限于腾讯云API和它的deepseek模型 后台只需要设置对应的API url 、模型 、API key,就可以让插件调用…...
【免费分享无广告】刷视频助手抖音快手小红书视频号自动脚本刷视频养号
养号可做极速版刷视频任务支持最新版软件 【资 源 名 称】刷视频助手 【资 源 版 本】1.0.2 【资 源 大 小】11.66M 【资 源 系 统】安卓 【资 源 介 绍】刷视频养号助手,操作简单,就一个页面。亲测无广纯净,打开即用 ———————————…...
Javascript大致框架
一、JavaScript简介 JavaScript,简称JS,是一种高级的、解释型的编程语言,主要用于为网页添加动态功能。它最初由Netscape公司于1995年推出,最早名为LiveScript,后更名为JavaScript。尽管名字中带有“Java”࿰…...
Linux 权限
目录 一、Linux 权限概念 1.1 用户 1.2 用户切换 1.3 sudo指令提权 1.4 文件访问者的分类(人) 1.5 文件类型和访问权限(事物属性) 1.6 文件访问权限和相关设置方法 1) chmod 2)chown 3)chgrp 二…...
linux stm32mp157 GIC-V2 中断处理过程分析
/* ** 中断触发时,调用的 handle_arch_irq 入口地址。 ** 因为此时,挂接的就是 gic_handle_irq 函数!gic_handle_irq 是个全局函数指针, ** static void __exception_irq_entry gic_handle_irq(struct pt_regs *regs) ** 它是Lin…...
Linux安装RTL8215网卡驱动
一、下载RTL8215驱动 下载地址:https://www.realtek.com/Download/ToDownload?typedirect&downloadid3763 二、安装编译工具 找一台能够访问互联网的linux服务器,下载对应的deb包 sudo apt-get install --download-only make gcc bzip2这将只下…...
用 Python 分析 IRIS 数据集:概率论与数理统计实战教程
一、引言 在数据科学的世界里,IRIS 数据集就像是一位常客,频繁出现在各种数据分析和机器学习的案例中。它包含 150 条记录,分属 Setosa、Versicolour、Virginica 这 3 种鸢尾花类别,每条记录还带有萼片长度、萼片宽度、花瓣长度、…...
QT6 源(79):阅读与注释表盘 QDial 的源码,其是基类QAbstractSlider 的子类,及其刻度线的属性举例
(1)源代码来自于头文件 qdial . h : #ifndef QDIAL_H #define QDIAL_H#include <QtWidgets/qtwidgetsglobal.h>#include <QtWidgets/qabstractslider.h>QT_REQUIRE_CONFIG(dial);QT_BEGIN_NAMESPACEclass QDialPrivate; class Q…...
Python 函数装饰器和闭包(闭包)
本章内容: Python 如何计算装饰器句法 Python 如何判断变量是不是局部的 闭包存在的原因和工作原理 nonlocal 能解决什么问题 掌握这些基础知识后,我们可以进一步探讨装饰器: 实现行为良好的装饰器 标准库中有用的装饰器 实现一个参数化装饰器…...
linux种文件名usr的含义是什么?
在Linux操作系统中,/usr目录是一个核心且容易引发困惑的概念。它既承载着历史演变的痕迹,又在现代系统中承担着关键功能。本文将从定义、结构、历史演变及常见问题等角度,全面解析usr的含义及其在Linux中的作用。 一、usr的起源与定义 1. 词…...
2025年中国光电子器件产业链分析
中商情报网讯:光电子器件作为信息时代的“光子引擎”,正从通信领域向消费电子、工业、医疗等全场景渗透。未来,随着材料科学、光子集成技术的突破,光电子器件将进一步重塑人类的信息交互方式,成为数字经济的关键基础设…...
基于 jQuery 实现灵活可配置的输入框验证功能
在 Web 表单开发中,输入框验证是保障数据准确性和安全性的关键环节。无论是用户注册、信息提交还是数据录入场景,都需要对用户输入内容进行合法性检查。本文将介绍如何使用 HTML、CSS 和 jQuery 构建一个可灵活配置的输入框验证系统,轻松应对…...
GPU性能加速的隐藏魔法:Dual-Issue Warp Schedule全解析
一、先来点"前菜":什么是Warp Schedule? 想象你是一个GPU的老板(比如NVIDIA老黄),手下有几万个"线程员工"要管理。直接让几万人同时开会?那得疯。于是你发明了"Warp"&#…...
无人机 | 无人机设计概述
无人机设计是一个复杂的系统工程,涉及空气动力学、电子技术、材料科学、控制算法等多个领域的综合应用。以下是无人机设计的主要模块和关键要素概述: 一、总体设计目标 任务需求定义 用途:航拍、物流、农业、军事侦察、环境监测等性能指标&am…...
电子邮件相关协议介绍
0 Preface/Foreword 1 协议介绍 电子邮件包含的主要协议: SMTPPOPIMAP 1.1 SMPT SMPT: Simple Mail Transfer Protocol,电子邮件传输的标准协议,负责将邮件从发送方传输到接收方邮件服务器。 1.2 POP POP: Post Office Protoc…...
标题:试验台铁地板:革新之路
在铁路行业中,地板是一项至关重要的元素,直接关系到列车的安全、舒适性以及使用寿命。近年来,试验台铁地板的开发和应用成为了铁路行业的热门话题之一。试验台铁地板的出现标志着铁路行业在技术创新方面迈出了重要的一步。本文将从试验台铁地…...
【C++】grpc(一):安装
因为接触了一些第三方项目和其他原因,我需要了解一些 RPC 相关的知识,首选的就是 Google 的 gRPC 库了。 安装 依然是使用WSL,发行版为Ubuntu 22.04.5 LTS gRPC的官网如下:https://grpc.org.cn/docs/languages/cpp/quickstart/…...
精益数据分析(41/126):深入解读移动应用商业模式的关键指标与策略
精益数据分析(41/126):深入解读移动应用商业模式的关键指标与策略 在创业与数据分析的学习征程中,我们不断探索不同商业模式的核心要点,以挖掘其中的商业价值。今天,我依旧带着与大家共同进步的初心&#…...
Python字符串全解析:从基础操作到高级应用的技术指南
引言 Python字符串作为编程领域的核心数据类型,其丰富的操作方法直接影响代码效率和可维护性。本文基于Python 3.10环境,结合工程实践中的高频需求,深度解析12类字符串操作技术,涵盖驻留机制、高效查询、编码…...
直方图反向投影
目录 一、概念 二、OpenCV C实现 2.1 基础实现代码 2.2 优化实现(带遮罩) 三、应用场景 3.1 目标跟踪 3.2 图像分割 3.3 工业检测 四、性能优化技巧 一、概念 直方图反向投影(Histogram Back Projection)是一种基于概率的图…...
TCP/IP协议深度解析:从分层架构到TCP核心机制
TCP/IP协议深度解析:从分层架构到TCP核心机制 一、TCP/IP协议族架构与核心概念 1. 协议族四层架构概述 TCP/IP是互联网的基础架构,采用四层分层模型,每层分工明确,协同实现网络通信: 层次核心功能关键协议/技术典…...
【动态规划】子序列问题
个人主页 : zxctscl 专栏 【C】、 【C语言】、 【Linux】、 【数据结构】、 【算法】 如有转载请先通知 文章目录 前言1 300. 最长递增子序列(经典)3.1 分析3.2 代码 2 376. 摆动序列2.1 分析2.2 代码 3 673. 最长递增子序列的个数3.1 分析3.…...
【C++】什么是头文件?
在 C 中,头文件(通常以 .h 或 .hpp 为扩展名)是用于声明类、函数、变量、宏和其他代码结构的文件。头文件的主要目的是提供接口定义,使得这些声明可以在多个源文件(.cpp 文件)中共享,从而避免重…...
shell(9)
流程控制(if) 1.if判断 i.基本语法(单分支) if [ 条件判断式 ] then 代码 fi ii.基本语法(多分支) if [ 条件判断式 ] then 代码 elif [ 条件判断式 ] 代码 ...... fi 注意事项:[ 条…...
Java设计模式: 工厂模式与策略模式
Java设计模式: 工厂模式与策略模式 在软件开发领域,设计模式是一种可重复使用的解决方案,用来解决常见的设计问题。工厂模式和策略模式是常用的设计模式之一,它们能够帮助开发人员更好地组织和管理代码,提高代码的可维护性和可扩展…...
大模型微调Fine-tuning:从概念到实践的全面解析
目录 引言 一、什么是大模型微调? 1.1 预训练与微调的区别 1.2 微调的技术演进 二、为什么需要微调? 2.1 解决大模型的固有局限 2.2 微调的优势 三、主流微调方法 3.1 全参数微调 3.2 参数高效微调(PEFT) 四、微调实践指…...
5月5日日记
今天是假期的最后一天,早上爽睡到11点起床。其实九点多就醒了,但是不愿意起来,睡了俩小时又。起来之后就点了外卖,西红柿打卤面两个卤蛋,11块钱很实惠。 起来之后洗袜子,简单策划了一下红色合唱。 给电动…...
TopK题-快速选择方法
代码 class Solution {public int findKthLargest(int[] nums, int k) {//k 就是对应的是下标 n - k 的位置 也就是说我们要的是下标n-k的元素return quickselect(nums, 0, nums.length - 1, nums.length - k);}public int quickselect(int[] nums, int left, int right, int …...
【SpringBoot篇】详解短信验证码登录功能实现
一:需求分析与设计 1.1 发送短信验证码 (1)产品原型 (2)业务逻辑 (3)接口设计 1.2 短信验证码登录 (1)业务逻辑 …...
深入理解 Bash 中的 $‘...‘ 字符串语法糖
在 Bash 脚本编程中,字符串处理是不可或缺的一部分。为了让开发者更高效地处理特殊字符和控制字符,Bash 引入了一种独特的字符串语法糖:$(带单引号的 ANSI-C 风格字符串)。这种语法来源于 C 语言的 ANSI-C 标准&#x…...
机器人强化学习入门学习笔记(二)
基于上一篇的《机器人强化学习入门学习笔记》,在基于 MuJoCo 的仿真强化学习训练中,除了 PPO(Proximal Policy Optimization)之外,还有多个主流强化学习算法可用于训练机器人直行或其他复杂动作。 🧠 一、常见强化学习算法对比(可用于 MuJoCo) 算法类型特点适合场景PP…...
Vue3携手Echarts,打造炫酷数据可视化大屏
一、引言 在数字化时代,数据如同企业的血液,蕴含着巨大的价值。而如何将这些抽象的数据转化为直观、易懂的信息,以便更好地支持决策和展示成果,成为了众多开发者和企业关注的焦点。数据可视化大屏应运而生,它以直观、醒…...
Java Web项目部署指南2025
Java Web项目部署指南 适用场景:本地 Windows 开发打包 → 远程 Ubuntu 服务器部署(2025年最佳实践) 适合人群:Java Web初学者、运维新手、需要一站式部署流程的开发者 🚀 部署流程横向流程图 #mermaid-svg-aznXsajzfU…...
STC单片机与淘晶驰串口屏通讯例程之04【密码登录与修改】
大家好,我是『芯知识学堂』的SingleYork,上一讲笔者给大家介绍了STC单片机与淘晶驰串口屏通讯例程之03【单片机程序解析】,今天笔者要跟大家分享的淘晶驰串口屏的密码登录与密码修改功能的实现。 很多项目中,为了保护某些参数不被随意修改,往往需要增加密码来保护,这也是…...
青听音乐 1.0.6| 全网音乐免费听,无损下载,4条音源,界面简洁无广告
一款强大的音乐播放器,内部集成了相当丰富的功能,可以一键搜索任何想要的歌曲或歌手专辑,同时还支持下载和收藏,拥有非常流畅的速度,使用起来没有任何限制!软件自带有大厂的解析音源,运行非常稳…...
FISCO BCOS【初体验笔记】
飞梭区块链搭建初体验笔记 环境部署创建四个节点的飞梭区块链用的VMware17 centos 7.9 区块链是飞梭2.0用的webase-frontJava环境的正确安装Webase-front搭建 智能合约设计一点合约调试笔记 智能合约abi文件转为go文件后端项目配置相关工具linux常用命令(防忘记&…...
56.[前端开发-前端工程化]Day03-webpack构建工具
邂逅Webpack和打包过程 1 认识webpack工具 前端开发的流程 内置模块path path常见的API 在webpack中的使用 认识webpack 脚手架依赖webpack Webpack到底是什么呢 Webpack官方的图片 Vue项目加载的文件有哪些呢? Webpack的使用前提 Webpack的安装 2 webpack基本打包…...
两次解析格式化字符串 + 使用SQLAlchemy的relationship执行任意命令 -- link-shortener b01lersCTF 2025
题目描述: A fast and reliable link shortener service, with a new feature to add private links! 我们走一遍逻辑 注册 app.route("/register", methods[GET, POST]) def register(): """ 用户注册路由,处理用户注册请求ÿ…...
双目测量中的将视差图重投影成三维坐标图
双目测距主要步骤如下: 左右两张图片 → 匹配 → 得到视差图 disp; 使用 cv2.reprojectImageTo3D(disp, Q) 将视差图 重投影 成三维坐标图 → 得到 points_3d 什么是 points_3d? points_3d cv2.reprojectImageTo3D(disp, Q)points_3d.shap…...