Linux进程调度与管理:(五)进程的调度之调度节拍

《Linux6.5源码分析:进程管理与调度系列文章》

本系列文章将对进程管理与调度进行知识梳理与源码分析,重点放在linux源码分析上,并结合eBPF程序对内核中进程调度机制进行数据实时拿取与分析。

在进行正式介绍之前,有必要对文章引用进行提前说明。本系列文章参考了大量的博客、文章以及书籍:

  • 《深入理解Linux内核》

  • 《Linux操作系统原理与应用》

  • 《奔跑吧Linux内核》

  • 《深入理解Linux进程与内存》

  • 《基于龙芯的Linux内核探索解析》

  • 进程调度 - 标签 - LoyenWang - 博客园 (cnblogs.com)

  • 专栏文章目录 - 知乎 (zhihu.com)

  • Linux进程调度:探索内核核心机制

Linux进程调度与管理:(五)进程的调度之调度节拍

在之前的文章中,我们介绍了进程是如何被创建出来的(我称之为进程肉体重塑)、进程是如何加载并启动的(我称之为进程灵魂注入)、进程的调度时机(进程何时加入就绪队列、何时被调度上CPU),以及进程调度时执行进程切换的细节,具体详细信息请参考以下文章:

  • Linux 进程管理与调度:(零)预备知识
  • Linux 进程管理与调度:(一)进程的创建与销毁
  • Linux 进程调度与管理:(二)进程的加载与启动
  • Linux 进程调度与管理:(三)进程的调度之调度时机
  • Linux 进程调度与管理:(四)进程的调度之schedule进程切换

我们在本系列第三篇文章中介绍了进程调度时机,其中涉及到了进程何时加入就绪队列,何时触发调度,在介绍被动调度时讲到了调度时机中的何时触发调度,我们用一张图回忆一下,上一篇文章涉及到的调度时机(也就是何时调用schedule函数),其中涉及到调度节拍,每当调度节拍被触发,都会判断是否需要触发抢占。本篇文章将接着Linux 进程调度与管理:(三)进程的调度之调度时机对调度节拍展开讲讲。

在这里插入图片描述

1. 调度节拍/周期调度 scheduler_tick()

在Linux中有一套时钟节拍机制,计算机系统随着时钟节拍需要周期性地做很多事着,例如刷新屏幕、数据落盘、进程调度等。Linux每隔固定周期会发出timer interrupt (IRQ0),HZ用来定义每一秒有多少次timer interrupt

对于任务调度器来说,定时器驱动的调度节拍是一个很重要的调度时机。时钟节拍最终会调用调度类的task_tick,完成调度相关的工作,会在这里判断是否需要调度下一个任务来抢占当前CPU核。也会触发多核之间任务队列的负载均衡。保证不让忙的核忙死,闲的核闲死。调度节拍的核心入口是scheduler_tick

每隔固定的时间, 时钟中断会被触发一次,此时内核会依靠周期性的时钟中断来处理CPU的控制权,具体是Linux调度器的scheduler_tick()函数被调用;

当时钟中断被触发后,会经过如下的调用流程,最终调用scheduler_tick()函数;
在这里插入图片描述

我们看一下schedule_tick函数的执行流程, 其实就是在获取完所在cpu的就绪队列之后,调用当前调度类中的ops, task_tick函数执行, 最终再进行负载均衡操作;

在这里插入图片描述

其实scheduler_tick()函数主要是调用当前调度类中的task_tick函数执行相关操作;

void scheduler_tick(void)
{int cpu = smp_processor_id();//当前CPU号struct rq *rq = cpu_rq(cpu);//当前核的运行队列struct task_struct *curr = rq->curr;//该cpu上运行的进程struct rq_flags rf;unsigned long thermal_pressure;/*热压*/u64 resched_latency;if (housekeeping_cpu(cpu, HK_TYPE_TICK))arch_scale_freq_tick();// 如果是管理CPU,更新CPU频率缩放sched_clock_tick();		// 更新调度时钟/*1.更新运行队列的时钟及负载信息*/rq_lock(rq, &rf);update_rq_clock(rq);///*1.更新运行队列的时钟计数*/thermal_pressure = arch_scale_thermal_pressure(cpu_of(rq)); // 获取CPU热压力update_thermal_load_avg(rq_clock_thermal(rq), rq, thermal_pressure);// 更新热负载平均值/*2.判断是否需要调度下一个任务*  不同调度类使用对应的task_tick函数实现*  用于检查当前进程是否已经运行足够长时间,是否需要被调度出去;*/curr->sched_class->task_tick(rq, curr, 0);if (sched_feat(LATENCY_WARN))resched_latency = cpu_resched_latency(rq);/*3.更新运行队列的cpu_load数组*/calc_global_load_tick(rq);sched_core_tick(rq);task_tick_mm_cid(rq, curr);
esched_latency_warn(cpu, resched_latency);perf_event_task_tick();/*5. 工作队列线程,更新其状态*/if (curr->flags & PF_WQ_WORKER)wq_worker_tick(curr);#ifdef CONFIG_SMP/*6.触发SMP负载均衡*/rq->idle_balance = idle_cpu(cpu);trigger_load_balance(rq);//触发一个软中断,让ksoftirq线程处理真正地负载均衡过程
#endif
}
  • 时钟中断处理程序中,调用 schedule_tick()函数;
  • 时钟中断是调度器的脉搏,内核依靠周期性的时钟来处理器CPU的控制权;
  • 时钟中断处理程序,检査当前进程的执行时间是否超额,如果超额则设置重新调度标志TIF NEED RESCHED
  • 时钟中断处理函数返回时,被中断的进程如果在用户模式下运行,需要检查是否有重新调度标志,设置了则调用schedule()调度 (也就是我们再第三篇文章Linux 进程调度与管理:(三)进程的调度之调度时机中介绍的调度执行时机)
  • 如果系统开启了SMP,则会触发负载均衡load_balance()

这里的核心函数是task_tick(rq, curr, 0), 是调用当前调度类中的task_tick函数实现,不同调度类对应不同的task_tick实现方法;

在这里插入图片描述

2. task_tick_fair()

这里以cfs这个调度类为例子,分析该调度类对应的task_tick函数—>task_tick_fair():

在这里插入图片描述

//更新当前任务 及其 相关调度实体的状态信息;
static void task_tick_fair(struct rq *rq, struct task_struct *curr, int queued)
{struct cfs_rq *cfs_rq;struct sched_entity *se = &curr->se;//curr为当前cpu上运行的进程/*1.遍历当前任务所有调度实体;*	(牵扯到组调度机制,需分情况)*	1.1如果系统实现了组调度机制,则遍历当前进程调度实体以及上一级调度实体;*	1.2如果未开启组调度机制,则仅遍历当前进程调度实体*/for_each_sched_entity(se) {cfs_rq = cfs_rq_of(se);/*1.3 更新调度实体的状态,检查是否需要调度*/entity_tick(cfs_rq, se, queued);}/*2.执行NUMA负载均衡,尝试将任务迁移到与其所需内存靠近的节点,通过调用task_tick_numa实现*/if (static_branch_unlikely(&sched_numa_balancing))/*触发时,执行NUMA负载均衡逻辑*/task_tick_numa(rq, curr);update_misfit_status(curr, rq);update_overutilized_status(task_rq(curr));/*3.执行核心调度相关操作逻辑*/task_tick_core(rq, curr);
}

此处先是遍历当前进程的所有调度实体==(如果开启了组调度机制,则遍历当前进程调度实体和上一级调度实体;如果没开启组调度机制,则仅遍历当前进程调度实体)==,并通过核心函数entity_tick函数更新调度实体的状态,并检查当前进程是否需要调度;

这里可以通过查看对for_each_sched_entity()的定义,来理解到底遍历了哪些调度实体:

  • 对于开启了组调度机制的,for_each_sched_entity()的定义是:

    #ifdef CONFIG_FAIR_GROUP_SCHED
    //会从当前调度实体开始,持续遍历其上一级的调度实体,直到se==NULL
    #define for_each_sched_entity(se) \for (; se; se = se->parent)
    

    会从当前调度实体开始,持续遍历其上一级的调度实体,直到se==NULL;

  • 对于未开启组调度机制的,for_each_sched_entity()`的定义是:

    #else	/* !CONFIG_FAIR_GROUP_SCHED */
    //仅遍历当前调度实体,随后se==NULL
    #define for_each_sched_entity(se) \for (; se; se = NULL)
    

    仅遍历当前进程的调度实体;

此处的重点就是entity_tick(cfs_rq, se, queued);,该函数会更新遍历到的调度实体的状态,并检查是否需要调度;接下来将围绕entity_tick做进一步分析。

2.1 entity_tick()更新时间信息,检查抢占

该函数主要就做了两件事:①更新调度实体的各种时间信息;②检查是否需要调度(抢占当前任务)

static void
entity_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr, int queued)
{/** 1.更新当前任务的各种时间信息;(当前进程的vruntime以及该就绪队列的min_vruntime)*/update_curr(cfs_rq);/** 2.更新当前进程的负载以及就绪队列的负载信息load_avg;*/update_load_avg(cfs_rq, curr, UPDATE_TG);/* 3.更新调度组的负载信息*/update_cfs_group(curr);#ifdef CONFIG_SCHED_HRTICK/*关于高精度定时器的相关处理逻辑*/
#endif/*4.check_preempt_tick检查是否需要抢占当前任务*/if (cfs_rq->nr_running > 1)//如果当前队列只有一个任务,则不执行,因为抢占逻辑不适用check_preempt_tick(cfs_rq, curr);//比较当前任务的vruntime和其他任务的vruntime来判断要不要抢占
}

梳理一下entity_tick的核心函数:

  • 【重点】通过update_curr()函数更新当前任务的各种时间信息,后面会详细分析这个函数;
  • 通过update_load_avg()以及update_cfs_group()函数来更新进程负载以及调度组负载信息,为负载均衡做准备;
  • 【重点】通过check_preempt_tick()判断是否需要抢占当前任务,这个是核心实现函数,后面会详细分析这个函数;
2.1.1 更新时间信息update_curr()

该函数主要用于更新计算进程的各种时间信息,主要进行了两步计算:

  • curr->sum_exec_runtime += delta_exec;计算出当前就绪队列上运行的进程本次运行的时间;
  • 通过calc_delta_fair(delta_exec, curr);计算出运行的虚拟时间;

在这里插入图片描述

除了以上的两个核心计算步骤,还针对cfs队列以及cgroup组进行了信息更新;

static void update_curr(struct cfs_rq *cfs_rq)
{/*1.计算当前进程运行了多少时间*/delta_exec = now - curr->exec_start;//自上次调度以来的时间curr->exec_start = now;//记录本次调度的时间/*2.更新任务的最大执行时间片*//*3.累加当前进程的总执行时间*/curr->sum_exec_runtime += delta_exec;//自进程创建以来累计运行时间schedstat_add(cfs_rq->exec_clock, delta_exec);//当前cfs队列总执行时间,所有任务的运行时间/*4.更新当前任务的虚拟运行时间*  calc_delta_fair来计算虚拟时间的增量*  update_min_vruntime更新CFS队列的最小虚拟时间*/curr->vruntime += calc_delta_fair(delta_exec, curr);update_min_vruntime(cfs_rq);/*5.更新相关统计信息*/if (entity_is_task(curr)) {struct task_struct *curtask = task_of(curr);/*一个可以获取的当前进程运行时间,运行虚拟时间的tracepoint*/trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);/*统计cgroup的相关信息*/cgroup_account_cputime(curtask, delta_exec);account_group_exec_runtime(curtask, delta_exec);}/*更新CFS队列的运行时间*/account_cfs_rq_runtime(cfs_rq, delta_exec);
}

值得注意的是,trace_sched_stat_runtime(curtask, delta_exec, curr->vruntime);为我们提供了一个tracepoint,用于获取当前进程运行的时间以及虚拟时间;

接下来对于calc_delta_fair()函数如何计算虚拟时间的,进行进一步分析:

static inline u64 calc_delta_fair(u64 delta, struct sched_entity *se)
{/*1.计算真正的虚拟时间*	1.1 nice = 0 时,权重为1024,即虚拟时间等于真实时间,直接跳过计算返回delta;*	1.2 nice!= 0 时,通过__calc_delta计算虚拟时间,并返回虚拟时间;*/if (unlikely(se->load.weight != NICE_0_LOAD))delta = __calc_delta(delta, NICE_0_LOAD, &se->load);return delta;
}

其中__calc_delta()函数主要通过如下算法计算虚拟时间:

vruntime = (时间运行时间 * ((NICE__LOAD*2^32)/weight))>>32

2.1.2 检查抢占check_preempt_tick()

该函数主要用于判断是否需要抢占;主要通过以下几个步骤实现判断:

  • 1.实际运行时间大于理想运行时间,则需重新调度
    • 通过sched_slice函数计算出当前任务预期运行时间,具体实现方式会在后面提到;
    • 实际运行时间大于预期运行时间则重新调度resched_curr
  • 2.避免当前任务运行时间太短,如果当前进程运行时间太短,则继续运行该任务,跳出抢占判断;
    • 将当前任务实际运行时间与sysctl_sched_min_granularity(任务调度的最小时间粒度)作对比;
  • 3.当前进程虚拟运行时间与就绪队列中最优先任务的虚拟时间做比较,若大出一定范围,则需重新调度;
    • 前面1步是保证当前任务实际运行时间不要太多;2步是当前任务实际运行时间不要太少;将实际运行时间限定在一定范围内;
    • 前面的1,2步均是拿当前进程的实际运行时间进行对比,而这里是对虚拟运行时间进行评判;

在这里插入图片描述

static void
check_preempt_tick(struct cfs_rq *cfs_rq, struct sched_entity *curr)
{unsigned long ideal_runtime, delta_exec;struct sched_entity *se;s64 delta;/**1.当前进程实际运行的时间比预期时间长*1.1 通过sched_slice计算当前任务理想时间片长度,赋值给ideal_runtime;*1.2 检查进程运行时间 是否超出 预期运行时间*/ideal_runtime = min_t(u64, sched_slice(cfs_rq, curr), sysctl_sched_latency);delta_exec = curr->sum_exec_runtime - curr->prev_sum_exec_runtime;//当前进程本次实际运行时间if (delta_exec > ideal_runtime) {//实际运行时间超出预期,则重新调度resched_currresched_curr(rq_of(cfs_rq));/** 清除调度器中的“亲密任务”(buddy)信息,* 避免当前任务因调度优先级偏好被再次选中。*/clear_buddies(cfs_rq, curr);return;}/** 2.避免当前进程运行时间太短;* sysctl_sched_min_granularity是任务调度的最小时间粒度* 如果实际运行时间小于最小时间粒度,说明其运行时间不足,* 不满足重新调度的要求,直接退出抢占判断*/if (delta_exec < sysctl_sched_min_granularity)return;/** 3. 当前进程运行的时间比预期时间大一定幅度,则需抢占;* 3.1 先计算出 当前任务虚拟时间 与 cfs队列中最优先任务(即红黑树左下角的任务)虚拟时间 之间的差;* 3.2 若 当前任务虚拟时间 < 最优先任务虚拟时间,则说明公平性未得到破坏,继续运行当前任务;* 3.3 若 当前任务虚拟时间 > 最优先任务虚拟时间,但超出的时间在一定范围内*     (超出时间小于一个调度周期ideal_runtime),继续运行当前任务;* 3.4 若 超出时间太多(即超出时间大于一个调度周期ideal_runtime),需要重新调度;*/se = __pick_first_entity(cfs_rq);//获取cfs调度队列中虚拟时间最小的任务delta = curr->vruntime - se->vruntime;//计算当前任务与最优先任务之间的虚拟运行时间差。if (delta < 0)//无需抢占,因为当前任务的虚拟时间比cfs队列中最优先任务的虚拟时间还小return;//超出的时间 都大于 预期运行时间,则重新调度;if (delta > ideal_runtime)resched_curr(rq_of(cfs_rq));
}

不难看出,这里的核心函数是sched_slice()resched_curr(),下面将详细分析这两个函数。

sched_slice():该函数根据当前系统的负载计算出一个调度周期,也即前面提到的预期运行时间,作为一个评判标准,以免当前任务运行时间过长;

首先是shced_slice()函数:调用__sched_period()函数计算单个调度周期;再循环遍历任务中的所有调度实体,计算slice预期运行时间;

static u64 sched_slice(struct cfs_rq *cfs_rq, struct sched_entity *se)
{/*1.计算出一个调度周期__sched_period()*/slice = __sched_period(nr_running + !se->on_rq);/*2.遍历任务的所有调度实体,计算时间片*/for_each_sched_entity(se) {struct load_weight *load;struct load_weight lw;struct cfs_rq *qcfs_rq;/*获取当前cfs队列的总负载*/qcfs_rq = cfs_rq_of(se);load = &qcfs_rq->load;if (unlikely(!se->on_rq)) {lw = qcfs_rq->load;update_load_add(&lw, se->load.weight);load = &lw;}/*根据当前调度实体的权重,计算其分配到的时间片长度*/slice = __calc_delta(slice, se->load.weight, load);/*							当前调度实体权重   队列总负载*/}return slice;
}

关于__sched_period()函数是如何实现的,他通过将就绪队列中的任务数与一个固定值sched_nr_latency作对比,来使用不同的策略,如果就绪队列中的任务少于sched_nr_latency,则直接使用系统默认的sysctl_sched_latency作为一个调度周期,如果就绪队列中的任务多于sched_nr_latency,则将nr_running * sysctl_sched_min_granularity作为一个调度周期,其中sysctl_sched_min_granularity是最小时间片,也即就绪队列中所有任务都运行最小时间片后作为一个调度周期。

/** This value is kept at sysctl_sched_latency/sysctl_sched_min_granularity*/
static unsigned int sched_nr_latency = 8;
unsigned int sysctl_sched_min_granularity= 750000ULL;
unsigned int sysctl_sched_latency= 6000000ULL;static u64 __sched_period(unsigned long nr_running)
{	/*1.就绪队列中进程较多,每个任务运行最小时间粒度*/if (unlikely(nr_running > sched_nr_latency))return nr_running * sysctl_sched_min_granularity;else/*2. 就绪队列中进程较少,sysctl_sched_latency作为默认调度周期*/return sysctl_sched_latency;
}

**resched_curr()😗*该函数执行重新调度相关工作;

void resched_curr(struct rq *rq)
{struct task_struct *curr = rq->curr;//就绪队列当前进程int cpu;lockdep_assert_rq_held(rq);//确保运行队列被锁定;/*1.检查当前任务是否已经被标记为需要重新调度,防止重复标记*/if (test_tsk_need_resched(curr))return;/*2.重新调度相关工作:*	2.1运行队列所属cpu是当前cpu,即处理本地cpu情况:*	   set_tsk_need_resched(curr)更改 task_struct下面thread_info->flag为TIF_NEED_RESCHED;*	   set_preempt_need_resched()设置内核的抢占标志位,允许调度器在下一次中断时触发任务切换*/cpu = cpu_of(rq);if (cpu == smp_processor_id()) {set_tsk_need_resched(curr);set_preempt_need_resched();return;}/*2.重新调度相关工作:*	2.2处理远程CPU情况:*	   set_nr_and_not_polling(curr)标记当前任务为TASK_RUNNING并判断目标CPU是否是空闲轮询状态*	   smp_send_reschedule(cpu)发送信号,通知目标 CPU 触发调度操作。*/if (set_nr_and_not_polling(curr))smp_send_reschedule(cpu);elsetrace_sched_wake_idle_without_ipi(cpu);
}

resched_curr()重新调度相关工作主要分以下两个情况:

  • 本地CPU:即目标运行队列所属CPU就是当前CPU
    • 这种情况更改当前任务的thread_info标识符中的TIF_NEED_RESCHED;
    • set_preempt_need_resched()设置内核的抢占标志位;
  • 远程CPU:即目标任务所属cpu不是当前cpu;
    • smp_send_reschedule(cpu)发送信号,通知目标 CPU 触发调度操作;

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

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

相关文章

K8S学习之基础十七:k8s的蓝绿部署

蓝绿部署概述 ​ 蓝绿部署中&#xff0c;一共有两套系统&#xff0c;一套是正在提供服务的系统&#xff0c;一套是准备发布的系统。两套系统都是功能完善、正在运行的系统&#xff0c;只是版本和对外服务情况不同。 ​ 开发新版本&#xff0c;要用新版本替换线上的旧版本&…

【定制开发】碰一碰发视频系统定制开发,支持OEM

在短视频营销爆发的2025年&#xff0c;"碰一碰发视频"技术已成为实体商家引流标配。某连锁餐饮品牌通过定制化开发&#xff0c;单月视频发布量突破10万条&#xff0c;获客成本降低80%&#xff01;本文将深入解析该系统的技术架构与开发要点&#xff0c;助你快速搭建高…

[Lc7_分治-快排] 快速选择排序 | 数组中的第K个最大元素 | 库存管理 III

目录 1. 数组中的第K个最大元素 题解 代码 2.库存管理 III 代码 1. 数组中的第K个最大元素 题目链接&#xff1a;215. 数组中的第K个最大元素 题目分析&#xff1a; 给定整数数组 nums 和整数 k&#xff0c;请返回数组中第 k 个最大的元素。 请注意&#xff0c;你需要…

AI视频生成工具清单(附网址与免费说明)

以下是一份详细的AI视频制作网站总结清单&#xff0c;包含免费/付费信息及核心功能说明&#xff1a; AI视频生成工具清单&#xff08;附网址与免费说明&#xff09; 1. Synthesia 网址&#xff1a;https://www.synthesia.io是否免费&#xff1a;免费试用&#xff08;生成视频…

dp_走方格(包含dfs分析,记忆化搜索)

类似题目解析&#xff1a;dp_最长上升子序列&#xff08;包含dfs分析&#xff0c;记忆化搜索&#xff09;-CSDN博客 题目链接&#xff1a;2067. 走方格 - AcWing题库 题目图片&#xff1a; 分析题目&#xff08;dfs&#xff09; 这个题目说有一个行为n行&#xff0c;列为m列…

Windows系统安装python2025最新安装包,包括环境配置,以及安装python编程软件PyCharm2024.3.3免费社区版本,详细全流程

一、python安装包安装 1、python安装包下载 浏览器打开官网&#xff0c;最好是谷歌浏览器 https://www.python.org/downloads/windows/ 下载安装包&#xff08;注意处理器是32位还是64位&#xff09; 注意&#xff1a;下载完成后&#xff0c;找到安装包并双击运行。在安装向导…

【GPT入门】第3课 客服会话质检(思维链)

【GPT入门】第3课 客服会话质检 1.质检任务2. 代码3.核心 1.质检任务 任务本质是检查客服与用户的对话是否有不合规的地方 质检是电信运营商和金融券商大规模使用的一项技术 每个涉及到服务合规的检查点称为一个质检项 我们选一个质检项&#xff0c;产品信息准确性&#xff0…

ubuntu 20.04 C++ 源码编译 cuda版本 opencv4.5.0

前提条件是安装好了cuda和cudnn 点击下载&#xff1a; opencv_contrib4.5.0 opencv 4.5.0 解压重命名后 进入opencv目录&#xff0c;创建build目录 “CUDA_ARCH_BIN ?” 这里要根据显卡查询一下,我的cuda是11&#xff0c;显卡1650&#xff0c;所以是7.5 查询方法1&#xff1…

K8s 1.27.1 实战系列(四)验证集群及应用部署测试

一、验证集群可用性 1、检查节点 kubectl get nodes ------------------------------------------------------ NAME STATUS ROLES AGE VERSION k8s-master Ready control-plane 3h48m v1.27.1 k8s-node1 Ready <none> …

【C++设计模式】第七篇:桥接模式(Bridge)

注意&#xff1a;复现代码时&#xff0c;确保 VS2022 使用 C17/20 标准以支持现代特性。 抽象与实现的解耦之道 1. 模式定义与用途​​ 核心思想​ ​桥接模式&#xff1a;将抽象部分与实现部分分离&#xff0c;使二者可以独立变化。​关键用途&#xff1a; ​1.拆分复杂继承…

在 Spring Boot 2.7.x 中引入 Kafka-0.9 的实践

文章目录 在 Spring Boot 2.7.x 中引入 Kafka-0.9 的实践一、下载 Kafka-0.9二、启动 Zookeeper 和 Kafka三、创建 Spring Boot 项目四、引入 kafka 依赖五、移除 Kafka 自动配置六、编写 Kafka 生产者6.1 Kafka配置类6.2 生产者监听类 七、编写Controller发送Kafka八、验证消费…

字符串中的数字之和

题目描述 程序要求能够提取输入的字符串中的数字&#xff0c;将数字累加&#xff0c;得到数字之和&#xff0c;如输入的字符串为"abc76wet23er1.",应该提取数字76,23,1,求和后&#xff0c;即76231100。 输入格式: 输入一个字符串&#xff0c;字符串长度不超过100.…

77.ObservableCollection使用介绍1 C#例子 WPF例子

可观察集合ObservableCollection using System; using System.Collections.ObjectModel;class Program {static void Main(){// 创建一个可观察集合ObservableCollection<string> list new ObservableCollection<string>();// 注册集合变化事件list.CollectionCh…

ORACLE 执行查询语句慢(不走对应索引)

1. 索引未被创建或未正确创建 确保为查询中涉及的列创建了索引。例如&#xff0c;如果你经常需要按column_name列进行查询&#xff0c;确保已经为该列创建了索引,索引创建语句 CREATE INDEX idx_column_name ON table_name(column_name); 2、索引不可用 原因:索引可能被标记为不…

r1-reasoning-rag:一种新的 RAG 思路

最近发现了一个开源项目&#xff0c;它提供了一种很好的 RAG 思路&#xff0c;它将 DeepSeek-R1 的推理能力结合 Agentic Workflow 应用于 RAG 检索 项目地址 https://github.com/deansaco/r1-reasoning-rag.git 项目通过结合 DeepSeek-R1、Tavily 和 LangGraph&#xff0c;实现…

服务器硬件配置统计

服务器型号和SN # dmidecode -t system | grep -E "Product Name|Serial Number" | awk -F: {print $2} PowerEdge R7515 4567CPU型号和物理CPU数量 echo "$(lscpu | grep "Model name" | cut -d : -f2 | sed s/^ *//) x $(lscpu | grep "Soc…

Hadoop、Spark、Flink Shuffle对比

一、Hadoop的shuffle 前置知识&#xff1a; Map任务的数量由Hadoop框架自动计算&#xff0c;等于分片数量&#xff0c;等于输入文件总大小 / 分片大小&#xff0c;分片大小为HDFS默认值128M&#xff0c;可调 Reduce任务数由用户在作业提交时通过Job.setNumReduceTasks(int)设…

Docker的常用镜像

Docker的常用镜像命令主要包括镜像的查看、搜索、拉取、删除、构建等操作&#xff0c;以下是综合多个来源的总结&#xff1a; 一、基础镜像操作 查看本地镜像 docker images• 显示所有本地镜像&#xff0c;包含仓库名&#xff08;REPOSITORY&#xff09;、标签&#xff08;TAG…

车载以太网测试-3【Wireshark介绍】

1 摘要 Wireshark 是一款开源的网络协议分析工具&#xff0c;广泛用于网络故障排查、协议分析、网络安全检测等领域。它能够捕获网络数据包&#xff0c;并以详细的、可读的格式显示这些数据包的内容。广泛应用于车载网络测试&#xff0c;是车载网络测试工程师必须掌握的工具。…

基于跨模态地图学习的视觉语言导航

前言 本工作开展的背景&#xff1a; 人类和其他物种构建类似地图的环境表示来完成寻路&#xff1a; &#xff08;1&#xff09;当人类只使用现成的驾驶或步行路径到达目标时&#xff0c;构建认知地图和获取空间知识的能力就会下降&#xff1b; &#xff08;2&#xff09;另…