Wakeup Source框架设计与实现

Wakeup Source 为系统组件提供了投票机制,以便低功耗子系统判断当前是否可以进入休眠。

Wakeup Source(后简称:WS) 模块可与内核中的其他模块或者上层服务交互,并最终体现在对睡眠锁的控制上。

通用低功耗软件栈.png

1. 模块功能说明

WS的处理逻辑基本上是围绕 combined_event_count 变量展开的,这个变量高16位记录系统已处理的所有的唤醒事件总数,低16位记录在处理中的唤醒事件总数。每次持锁时,处理中的唤醒事件记录(低16位)会加1;每次释放锁时,处理中的唤醒事件记录(低16位)会减1,同时已处理的唤醒事件记录(高16位)会加1。

对于每次系统能否进入休眠,通过判断是否有正在处理中的唤醒事件(低16位)来决定。该模块实现主要的功能:

  • 持锁和释放锁

  • 注册和注销锁

  • 查询激活状态锁个数

2. 主要数据结构

2.1 wakeup_source 结构体

@include/linux/pm_wakeup.h
/*** struct wakeup_source - Representation of wakeup sources** @name: Name of the wakeup source* @id: Wakeup source id* @entry: Wakeup source list entry* @lock: Wakeup source lock* @wakeirq: Optional device specific wakeirq* @timer: Wakeup timer list* @timer_expires: Wakeup timer expiration* @total_time: Total time this wakeup source has been active.* @max_time: Maximum time this wakeup source has been continuously active.* @last_time: Monotonic clock when the wakeup source's was touched last time.* @prevent_sleep_time: Total time this source has been preventing autosleep.* @event_count: Number of signaled wakeup events.* @active_count: Number of times the wakeup source was activated.* @relax_count: Number of times the wakeup source was deactivated.* @expire_count: Number of times the wakeup source's timeout has expired.* @wakeup_count: Number of times the wakeup source might abort suspend.* @dev: Struct device for sysfs statistics about the wakeup source.* @active: Status of the wakeup source.* @autosleep_enabled: Autosleep is active, so update @prevent_sleep_time.*/
struct wakeup_source {const char 		*name; //ws 名称int			id;  //WS系统给本ws分配的IDstruct list_head	entry; //用于把本ws节点维护到WS系统的全局链表中spinlock_t		lock;struct wake_irq		*wakeirq; //与本ws节点绑定的唤醒中断相关的结构体,用户可自行把指定中断与ws绑定struct timer_list	timer; //超时锁使用,如定义本ws为超时锁,指定在一定时间后释放锁unsigned long		timer_expires;//超时锁超时时间ktime_t total_time; //本ws激活的总时长ktime_t max_time;   //在ws激活历史中,最长一次的激活时间ktime_t last_time;  //最后一次访问本ws的时间ktime_t start_prevent_time; //本ws最近一次阻止autosleep进入休眠的时间戳ktime_t prevent_sleep_time; //因本ws导致的阻止autosleep进入休眠的总时间unsigned long		event_count; //事件次数,本ws被持锁(不考虑是否已持锁),则加1并作记录unsigned long		active_count;//激活次数,本ws仅在首次持锁(激活)时加1(已持锁则不加1,锁释放后再次持锁则加1)unsigned long		relax_count; //释放次数,与 active_count 相对unsigned long		expire_count; //超时锁超时次数unsigned long		wakeup_count; //与event_count一样,但受events_check_enabled 使能标记控制struct device		*dev; //与本ws绑定的设备bool			active:1; //标记是否处于激活状态bool			autosleep_enabled:1; //标记是否使能autosleep
};

2.2 核心变量

2.2.1 combined_event_count 变量

static atomic_t combined_event_count = ATOMIC_INIT(0);该变量是1个组合计数变量,高16位记录唤醒事件的总数,低16位记录正在处理中的唤醒事件的总数。系统根据低16位(正在处理中的唤醒事件)来判断是否可以进入休眠。

2.2.2 wakeup_sources 变量

static LIST_HEAD(wakeup_sources);所有通过调用 wakeup_source_register()注册的ws全部维护在此链表中,以便系统进行维护。

2.3 主要函数分析

Wakeup Source 对外提供的主要接口:

  • wakeup_source_register()wakeup_source_unregister()分别用于注册与注销一个ws

  • __pm_stay_awake()__pm_relax(),针对ws类型对象提供持锁与释放锁接口

  • (device_set_wakeup_capable()+device_wakeup_enable()/device_wakeup_disable()/device_set_wakeup_enable())/device_init_wakeup()给设备配置是否支持唤醒以及注册/注销ws的接口

  • pm_stay_awake()pm_relax(),针对device类型对象提供持锁与释放锁接口

2.3.1 wakeup_source_register()/wakeup_source_unregister() 接口

wakeup_source_register()函数为dev设备创建ws,并将创建的ws添加到全局链表wakeup_sources中,方便后续维护,并在sysfs系统中创建节点/sys/class/wakeup/wakeup<id>/,便于获取ws相关信息。

@drivers/base/power/wakeup.c
/*** wakeup_source_register - Create wakeup source and add it to the list.* @dev: Device this wakeup source is associated with (or NULL if virtual).* @name: Name of the wakeup source to register.*/
struct wakeup_source *wakeup_source_register(struct device *dev,const char *name)
{struct wakeup_source *ws;int ret;ws = wakeup_source_create(name); //分配内存,设置ws的name和idif (ws) {if (!dev || device_is_registered(dev)) {//在sysfs下为该ws创建dev, /sys/class/wakeup/wakeup<id>/ret = wakeup_source_sysfs_add(dev, ws);if (ret) {wakeup_source_free(ws);return NULL;}}wakeup_source_add(ws); //设置超时回调函数并将ws添加到wakeup_sources链表}return ws;
}
@drivers/base/power/wakeup_stats.c
static struct device *wakeup_source_device_create(struct device *parent,struct wakeup_source *ws)
{struct device *dev = NULL;int retval = -ENODEV;dev = kzalloc(sizeof(*dev), GFP_KERNEL);device_initialize(dev);dev->devt = MKDEV(0, 0);dev->class = wakeup_class; //ws dev挂于wakeup类dev->parent = parent;dev->groups = wakeup_source_groups;dev->release = device_create_release;dev_set_drvdata(dev, ws);device_set_pm_not_required(dev);retval = kobject_set_name(&dev->kobj, "wakeup%d", ws->id);retval = device_add(dev);return dev;
}
//ws dev存在的属性: /sys/class/wakeup/wakeup<id>/
static struct attribute *wakeup_source_attrs[] = {&dev_attr_name.attr, //RO, ws 名称&dev_attr_active_count.attr, //RO, 激活次数&dev_attr_event_count.attr, //RO, 持锁次数&dev_attr_wakeup_count.attr, //RO, 同event_count,但受events_check_enabled使能标记&dev_attr_expire_count.attr, //RO, 超时次数&dev_attr_active_time_ms.attr, //RO, 如当前处于激活状态,显示已激活时间&dev_attr_total_time_ms.attr, //RO, 总激活时间&dev_attr_max_time_ms.attr, //RO, 最长激活时间&dev_attr_last_change_ms.attr, //RO, 最近一次激活时的时间戳&dev_attr_prevent_suspend_time_ms.attr, //RO, 阻止autosleep进入休眠的总时间NULL,
};
ATTRIBUTE_GROUPS(wakeup_source);

wakeup_source_unregister() 接口删除了已注册的ws,移除了sysfs系统中的节点并释放占用的系统资源。

@drivers/base/power/wakeup.c
void wakeup_source_unregister(struct wakeup_source *ws)
{if (ws) {wakeup_source_remove(ws); //从wakeup_sources队列移除并删除其定时器if (ws->dev)wakeup_source_sysfs_remove(ws);//移除该ws在sysfs系统中的信息wakeup_source_destroy(ws);}
}
void wakeup_source_destroy(struct wakeup_source *ws)
{__pm_relax(ws); //释放该wswakeup_source_record(ws);//如果该ws被持锁过,则将其记录叠加到deleted_ws这个ws上wakeup_source_free(ws);//释放内存资源
}static struct wakeup_source deleted_ws = {//用于保存已移除ws的记录.name = "deleted",.lock =  __SPIN_LOCK_UNLOCKED(deleted_ws.lock),
};static void wakeup_source_record(struct wakeup_source *ws)
{unsigned long flags;spin_lock_irqsave(&deleted_ws.lock, flags);if (ws->event_count) {//如果该ws被持锁过,则将记录都叠加到deleted_ws这个ws上deleted_ws.total_time =ktime_add(deleted_ws.total_time, ws->total_time);deleted_ws.prevent_sleep_time =ktime_add(deleted_ws.prevent_sleep_time,ws->prevent_sleep_time);deleted_ws.max_time =ktime_compare(deleted_ws.max_time, ws->max_time) > 0 ?deleted_ws.max_time : ws->max_time;deleted_ws.event_count += ws->event_count;deleted_ws.active_count += ws->active_count;deleted_ws.relax_count += ws->relax_count;deleted_ws.expire_count += ws->expire_count;deleted_ws.wakeup_count += ws->wakeup_count;}spin_unlock_irqrestore(&deleted_ws.lock, flags);
}

2.3.2 __pm_stay_awake()/__pm_relax() 接口

__pm_stay_awake() 用于上锁ws来阻止系统休眠。

@drivers/base/power/wakeup.c
void __pm_stay_awake(struct wakeup_source *ws)
{unsigned long flags;if (!ws)return;spin_lock_irqsave(&ws->lock, flags);wakeup_source_report_event(ws, false);//纪录该ws的信息del_timer(&ws->timer);ws->timer_expires = 0;spin_unlock_irqrestore(&ws->lock, flags);
}
static void wakeup_source_report_event(struct wakeup_source *ws, bool hard)
{ws->event_count++;  //持锁次数加1/* This is racy, but the counter is approximate anyway. */if (events_check_enabled)ws->wakeup_count++;if (!ws->active) //ws还未激活情况下,激活wswakeup_source_activate(ws);if (hard)  //如果需要,可以强制阻止系统休眠pm_system_wakeup();
}
static void wakeup_source_activate(struct wakeup_source *ws)
{unsigned int cec;if (WARN_ONCE(wakeup_source_not_registered(ws),"unregistered wakeup source\n"))return;ws->active = true;ws->active_count++;  //激活次数加1ws->last_time = ktime_get(); //纪录最后操作该锁的时间戳if (ws->autosleep_enabled) //如果autosleep已使能,则记录该ws阻止休眠时时间戳ws->start_prevent_time = ws->last_time;/* Increment the counter of events in progress. */cec = atomic_inc_return(&combined_event_count); //combined_event_count低16位加1trace_wakeup_source_activate(ws->name, cec);
}

__pm_relax() 用于将持有的睡眠锁释放掉,并在检测到combined_event_count低16位为0(表示当前没有在处理的ws)时会触发wakeup_count_wait_queue等待队列运行,如果工作队列满足睡眠条件,则继续进入睡眠流程,该机制是通过pm_get_wakeup_count()接口与autosleep配合使用的

@drivers/base/power/wakeup.c
void __pm_relax(struct wakeup_source *ws)
{unsigned long flags;if (!ws)return;spin_lock_irqsave(&ws->lock, flags);if (ws->active) //如果ws已激活,则去激活该wswakeup_source_deactivate(ws);spin_unlock_irqrestore(&ws->lock, flags);
}static void wakeup_source_deactivate(struct wakeup_source *ws)
{unsigned int cnt, inpr, cec;ktime_t duration;ktime_t now;ws->relax_count++; //释放次数加1/** __pm_relax() may be called directly or from a timer function.* If it is called directly right after the timer function has been* started, but before the timer function calls __pm_relax(), it is* possible that __pm_stay_awake() will be called in the meantime and* will set ws->active.  Then, ws->active may be cleared immediately* by the __pm_relax() called from the timer function, but in such a* case ws->relax_count will be different from ws->active_count.*/if (ws->relax_count != ws->active_count) {ws->relax_count--; //未解决定时锁与主动调用释放锁并发操作时出现冲突做的处理return;}ws->active = false;now = ktime_get();duration = ktime_sub(now, ws->last_time);ws->total_time = ktime_add(ws->total_time, duration); //叠加总的持锁时间if (ktime_to_ns(duration) > ktime_to_ns(ws->max_time))ws->max_time = duration;  //更新最长持锁时间ws->last_time = now; //纪录最后操作该锁的时间戳del_timer(&ws->timer);ws->timer_expires = 0;if (ws->autosleep_enabled)//如果autosleep已使能,更新该ws阻止系统休眠的时长update_prevent_sleep_time(ws, now);/** Increment the counter of registered wakeup events and decrement the* couter of wakeup events in progress simultaneously.*/cec = atomic_add_return(MAX_IN_PROGRESS, &combined_event_count);//combined_event_count高16位加1trace_wakeup_source_deactivate(ws->name, cec);split_counters(&cnt, &inpr);//拆分出combined_event_count高16位和低16位if (!inpr && waitqueue_active(&wakeup_count_wait_queue))//如果该ws已经无正在处理的唤醒事件,则通知PM corewake_up(&wakeup_count_wait_queue);
}

注:同个ws连续使用多次__pm_stay_awake()__pm_relax()只会增加/减少一次combined_event_count低16位(表示正在处理中的事件总数),只要__pm_relax()被调用就会释放锁。

2.3.3 pm_get_wakeup_count()接口

该函数主要是获取已处理的wakeup event数量(combined_event_count高16位)与正在处理的wakeup event数量是否为0(combined_event_count低16位)。

bool pm_get_wakeup_count(unsigned int *count, bool block)
{unsigned int cnt, inpr;if (block) { DEFINE_WAIT(wait); //定义名为wait的等待队列入口for (;;) {prepare_to_wait(&wakeup_count_wait_queue, &wait,TASK_INTERRUPTIBLE); //准备 wakeup_count_wait_queue 等待队列split_counters(&cnt, &inpr);if (inpr == 0 || signal_pending(current))break;pm_print_active_wakeup_sources();schedule(); //调度到其他线程}//__pm_relax() 里wake_up(&wakeup_count_wait_queue);会触发调度到此处finish_wait(&wakeup_count_wait_queue, &wait);}split_counters(&cnt, &inpr);*count = cnt;return !inpr; //返回0表示有待处理事件,返回1表示无待处理事件
}

1.如果入参block为0,则仅仅对入参count赋值当前已处理的wakeup event总数,并返回当前是否有待处理wakeup event(返回0表示有待处理事件,返回1表示无待处理事件)。2.如果入参block为1,则需要一直等到待处理事件为0(combined_event_count低16位为0)或者当前挂起进程有事件需要处理时才退出。该处理分支的wait等待队列会在__pm_relax()满足睡眠条件时触发调度运行,即finish_wait().

2.3.4 pm_wakeup_pending() 接口

该函数的功能是确认当前是否满足休眠条件,返回true表示可以休眠,false表示不可休眠。

bool pm_wakeup_pending(void)
{unsigned long flags;bool ret = false;raw_spin_lock_irqsave(&events_lock, flags);if (events_check_enabled) {unsigned int cnt, inpr;split_counters(&cnt, &inpr);ret = (cnt != saved_count || inpr > 0);events_check_enabled = !ret;}raw_spin_unlock_irqrestore(&events_lock, flags);if (ret) {pm_pr_dbg("Wakeup pending, aborting suspend\n");pm_print_active_wakeup_sources();}return ret || atomic_read(&pm_abort_suspend) > 0;
}

判断允许休眠的依据:1.已处理的wakeup event数量与已记录的数量(saved_count)一致,且2.待处理的wakeup event数量为0,且3.原子量pm_abort_suspend为0(该值大于0表示睡眠流程中出现了唤醒中断或事件,唤醒事件通过调用pm_system_wakeup()来给pm_abort_suspend加1操作。)

2.3.5 device与wakeup_source关联处理的接口

kernel抽象出的device数据结构存放着power manager相关的信息,其中就存放着wakeup source数据结构,如下:

//代码格式错误,仅为呈现数据结构,请忽略格式。
struct device {// @power:	For device power management.struct dev_pm_info	power {unsigned int		can_wakeup:1; //需置1才允许使用wakeup sourcestruct wakeup_source	*wakeup; };
};

wakeup source框架中为此提供了大量相关的接口直接操作某个dev的ws,接口如下:

  • int device_wakeup_enable(struct device *dev):注册设备的wakeup source1.以dev名注册个ws,并指定该ws dev的parent为当前dev2.将注册的ws关联到dev->power.wakeup,如果存在wakeirq,也会一起绑定到该ws上。

  • int device_wakeup_disable(struct device *dev):注销设备的wakeup source1.取消已注册的ws与dev->power.wakeup的关联2.注销ws

  • void device_set_wakeup_capable(struct device *dev, bool capable):设置设备是否支持wakeup source1.设置dev->power.can_wakeup2.如果设备支持wakeup,则为其创建属性文件(位于/sys/devices/<dev_name>/power/下);如果设备不支持wakeup,则不会移除相关属性文件。

static struct attribute *wakeup_attrs[] = {
#ifdef CONFIG_PM_SLEEP&dev_attr_wakeup.attr, //RW,可写入enabled/disabled动态配置是否支持wakeup&dev_attr_wakeup_count.attr, //RO, 读取该dev ws的wakeup_count&dev_attr_wakeup_active_count.attr, //RO, 读取该dev ws的active_count&dev_attr_wakeup_abort_count.attr, //RO, 读取该dev ws的wakeup_count&dev_attr_wakeup_expire_count.attr, //RO, 读取该dev ws的expire_count&dev_attr_wakeup_active.attr, //RO, 读取该dev ws的active状态&dev_attr_wakeup_total_time_ms.attr, //RO, 读取该dev ws的total_time&dev_attr_wakeup_max_time_ms.attr, //RO, 读取该dev ws的max_time&dev_attr_wakeup_last_time_ms.attr, //RO, 读取该dev ws的last_time
#ifdef CONFIG_PM_AUTOSLEEP&dev_attr_wakeup_prevent_sleep_time_ms.attr, //RO, 读取该dev ws的prevent_sleep_time
#endif
#endifNULL,
};

  • int device_init_wakeup(struct device *dev, bool enable):一步到位直接配置是否支持wakeup并且注册/注销ws

int device_init_wakeup(struct device *dev, bool enable)
{int ret = 0;if (enable) {device_set_wakeup_capable(dev, true);ret = device_wakeup_enable(dev);} else {device_wakeup_disable(dev);device_set_wakeup_capable(dev, false); }return ret;
}

  • int device_set_wakeup_enable(struct device *dev, bool enable):设置设备是否能通过ws唤醒系统,注册/注销ws

int device_set_wakeup_enable(struct device *dev, bool enable)
{return enable ? device_wakeup_enable(dev) : device_wakeup_disable(dev);
}

  • void pm_stay_awake(struct device *dev):持锁设备的ws,不让设备休眠,实际是调用__pm_stay_awake(dev->power.wakeup);实现

  • void pm_relax(struct device *dev):释放设备的ws,允许设备休眠,实际是调用__pm_relax(dev->power.wakeup);实现

总结:1.device_set_wakeup_capable() 用于设置是否支持wakeup,并提供属性节点,便于调试2.device_wakeup_enable()/device_wakeup_disable()/device_set_wakeup_enable()主要是注册/注销设备ws,需在device_set_wakeup_capable()enabled的前提下才能使用。3.device_init_wakeup() 通常使用在默认支持wakeup的device上,在probe/remove时分别enable/disable。4.pm_stay_awake()/pm_relax()主要是持有/释放ws锁,阻止/允许系统休眠

3. 主要工作时序

1)device或者其他需要上锁的模块调用device_init_wakeup()/wakeup_source_register()来注册ws2)在处理业务时,为了防止系统进入睡眠流程,设备或模块可以通过调用pm_stay_awake()/__pm_stay_awake()来持锁ws阻止休眠3)当业务处理完成后,设备或模块可以调用pm_relax()/__pm_relax()来释放ws允许系统休眠4)在__pm_relax()释放锁时,会检查当前是否有正在处理的持锁事件,如果没有,则触发wakeup_count_wait_queue5)wakeup_count_wait_queue所在的pm_get_wakeup_count()接口会返回到autosleep的工作队列中继续走休眠流程

image

4. 调试节点

  1. 获取所有wakeup source信息节点:cat /d/wakeup_sources列出所有wakeup_source当前的信息,包括:name,active_count,event_count,wakeup_count,expire_count,active_since,total_time,max_time,last_change,prevent_suspend_time。注:代码实现在@drivers/base/power/wakeup.c

  2. 从wakeup类下获取某个ws的信息:/sys/class/wakeup/wakeup<id>/wakeup类下汇总了所有已注册的ws,该节点下存在属性:name, active_count, event_count, wakeup_count,expire_count, active_time_ms, total_time_ms, max_time_ms, last_change_ms, prevent_suspend_time_ms。注:代码实现在@drivers/base/power/wakeup_stats.c

  3. 从device节点下获取该设备的ws信息:/sys/devices/<dev_name>/power/该节点存在如下属性信息:wakeup(是否支持唤醒),wakeup_count, wakeup_active_count, wakeup_abort_count, wakeup_expire_count, wakeup_active, wakeup_total_time_ms, max_time_ms, last_time_ms, prevent_sleep_time_ms。注:代码实现在@drivers/base/power/sysfs.c

注:本文是基于内核kernel-5.10展开。上述分析基于32位系统,若是64位系统,则combined_event_count会被拆分成2个32位分别来纪录唤醒事件的总数和正在处理中的唤醒事件的总数

文章转载自:Jayfan_Ma

原文链接:https://www.cnblogs.com/jiafan-ma/p/18200874

体验地址:引迈 - JNPF快速开发平台_低代码开发平台_零代码开发平台_流程设计器_表单引擎_工作流引擎_软件架构

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/24045.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

后端进阶-分库分表

文章目录 为什么需要分库为什么需要分表 什么时候需要分库分表只需要分库只需要分表 分库分表解决方案垂直分库水平分库垂直分表水平分表 分库分表常用算法范围算法hash分片查表分片 分库分表模式客户端模式代理模式 今天跟着训练营学习了分库分表&#xff0c;整理了学习笔记。…

echarts的使用

一 echarts的使用 引入 echarts.js 文件 <script src"https://cdn.jsdelivr.net/npm/echarts/dist/echarts.min.js"></script> 准备一个呈现图表的盒子 <div class"container"><div class"t_header"><span>端午…

智能视频监控平台LntonCVS视频融合共享平台保障露营安全解决方案

在当今社会&#xff0c;都市生活的快节奏和压力使得越来越多的人渴望逃离城市的喧嚣&#xff0c;寻求一种短暂的慢生活体验。他们向往在壮丽的山河之间或宁静的乡村中露营&#xff0c;享受大自然的宁静与美好。随着露营活动的普及&#xff0c;露营地的场景也变得更加丰富多样&a…

使用python绘制核密度估计图

使用python绘制核密度估计图 核密度估计图介绍效果代码 核密度估计图介绍 核密度估计&#xff08;Kernel Density Estimation&#xff0c;KDE&#xff09;是一种用于估计数据概率密度函数的非参数方法。与直方图不同&#xff0c;KDE 可以生成平滑的密度曲线&#xff0c;更好地…

LeetCode62不同路径

题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。问总共有多少条不同的路径&#xff1f; …

网络基础_02

1.ARP协议 地址解析协议&#xff08;Address Resolution Protocol&#xff09; 已知对方的三层ip地址&#xff0c;需要二层mac地址 当一台设备&#xff08;请求方&#xff09;需要知道某个 IP 地址对应的 MAC 地址时&#xff0c;会使用 ARP封装一个数据帧。这台设备的网络层以…

华为RH2288H V3服务器iBMC的SSL证书续期

本文对华为RH2288H V3服务器iBMC的SSL证书续期&#xff0c;以避名登录告警提示及主机状态异常。 一、检查现网服务器iBMC的SSL证书到期时间 登录iBMC&#xff0c;点击配置--SSL证书&#xff0c;如下&#xff1a; 可以看到本服务器SSL证书将于今年7月22日到期。 二、联系厂家…

【第四节】C/C++数据结构之树与二叉树

目录 一、基本概念与术语 二、树的ADT 三、二叉树的定义和术语 四、平衡二叉树 4.1 解释 4.2 相关经典操作 4.3 代码展示 一、基本概念与术语 树(Tree)是由一个或多个结点组成的有限集合T。其中: 1 有一个特定的结点&#xff0c;称为该树的根(root)结点&#xff1b; 2 …

【Linux】进程2——管理概念,进程概念

1.什么是管理&#xff1f; 那在还没有学习进程之前&#xff0c;就问大家&#xff0c;操作系统是怎么管理进行进程管理的呢&#xff1f; 很简单&#xff0c;先把进程描述起来&#xff0c;再把进程组织起来&#xff01; 我们拿大学为例子 最典型的管理者——校长最典型的被管理…

来自工业界的知识库 RAG 服务(三),FinGLM 竞赛获奖项目详解

背景介绍 前面介绍过工业界的 RAG 服务 QAnything 和 RagFlow 的详细设计&#xff0c;也介绍过来自学术界的 一些优化手段。 前一阵子刚好看到智谱组织的一个金融大模型比赛 FinGLM&#xff0c;主要做就是 RAG 服务的竞赛&#xff0c;深入研究了其中的几个获奖作品&#xff…

Pyramid Vision Transformer, PVT(ICCV 2021)原理与代码解读

paper&#xff1a;Pyramid Vision Transformer: A Versatile Backbone for Dense Prediction without Convolutions official implementation&#xff1a;GitHub - whai362/PVT: Official implementation of PVT series 存在的问题 现有的 Vision Transformer (ViT) 主要设计…

C++结合ffmpeg获取声音的分贝值

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、分贝是什么&#xff1f;1.功率量2.场量 二、实际操作1.分析wav文件2.读取麦克风 总结 前言 最近面对一个需求&#xff0c;就是需要传递声音文件到模型里推…

链表的回文结构OJ

链表的回文结构_牛客题霸_牛客网对于一个链表&#xff0c;请设计一个时间复杂度为O(n),额外空间复杂度为O(1)的算法&#xff0c;判断其是否为。题目来自【牛客题霸】https://www.nowcoder.com/practice/d281619e4b3e4a60a2cc66ea32855bfa?tpId49&&tqId29370&rp1&a…

CodeMeter助力Hilscher,推动实现全球智能制造连接解决方案

Hilscher的旗舰店为开放工业4.0联盟&#xff08;OI4&#xff09;社区提供了应用商店的便捷和开放性&#xff0c;将这一概念引入工业领域。该商店依托CodeMeter的许可证管理和加密保护&#xff0c;为工业用户提供了丰富的应用和解决方案库&#xff0c;满足他们在车间自动化和连接…

WPF中读取Excel文件的内容

演示效果 实现方案 1.首先导入需要的Dll(这部分可能需要你自己搜一下) Epplus.dll Excel.dll ICSharpCode.SharpZipLib.dll 2.在你的解决方案的的依赖项->添加引用->浏览->选择1中的这几个Dll点击确定。(添加依赖) 3.然后看代码内容 附上源码 using Excel; usi…

计网复习资料

一、选择题&#xff08;每题2分&#xff0c;共40分&#xff09; 1. Internet 网络本质上属于&#xff08; &#xff09;网络。 A.电路交换 B.报文交换 C.分组交换 D.虚电路 2.在 OSI 参考模型中,自下而上第一个提供端到端服务的是( )。 A.数据链路层 B.传输…

Thinkphp使用Elasticsearch查询

在Thinkphp中调用ES&#xff0c;如果自己手写json格式的query肯定是很麻烦的。我这里使用的是ONGR ElasticsearchDSL 构建 ES 查询。ongr ElasticsearchDSL 的开源项目地址&#xff1a;GitHub - ongr-io/ElasticsearchDSL: Query DSL library for Elasticsearch。ONGR Elastics…

100V 15A TO-252 N沟道MOS管 HC070N10L 惠海

MOS管的工作原理是基于在P型半导体与N型半导体之间形成的PN结&#xff0c;通过改变栅极电压来调整沟道内载流子的数量&#xff0c;从而改变沟道电阻和源极与漏极之间的电流大小。由于MOS管具有输入电阻高、噪声小、功耗低等优点&#xff0c;它们在大规模和超大规模集成电路中得…

package.json中resolutions的使用场景

文章目录 用途配置示例使用方法注意事项和peerDependencies有什么不同peerDependenciesresolutions 总结 ✍创作者&#xff1a;全栈弄潮儿 &#x1f3e1; 个人主页&#xff1a; 全栈弄潮儿的个人主页 &#x1f3d9;️ 个人社区&#xff0c;欢迎你的加入&#xff1a;全栈弄潮儿的…

git【工具软件】分布式版本控制工具软件

一、Git 的介绍 git软件的作用&#xff1a;管理软件开发项目中的源代码文件。 常用功能&#xff1a; 仓库管理、文件管理、分支管理、标签管理、远程操作 功能指令&#xff1a; add&#xff0c;commit&#xff0c;log&#xff0c;branch&#xff0c;tag&#xff0c;remote…