当前位置: 首页 > news >正文

LINUX系统CFS调度模型实现思考和仿真

关于LINUX资源调度

计算机系统中,管理资源的方式一般有两种方法,分别是时间分割和空间分割,可以通过分割硬件的相似性,让软件以一致的逻辑执行,CPU运行特点是在时刻点A和时刻B运行机制是一样的,不同的只是执行现场,可以看作是在时间上有对称性的一种资源,比较适合的管理方法就是分割时间。而对于内存这种资源,不同时刻点的数据是不可预期的,在时间尺度上没有相似性,但是在空间尺度上,A区和B区的晶体管作用基本上是一样的,只是空间范围的起始不同,就比较适合使用空间分割管理。

所以,在操作系统实现中,对于CPU分割时间的管理方式就是任务调度,而对于内存这种进行空间管理的手段就是BUDDY系统。

CPU是否能在空间维度上管理呢?或者内存是否可以在时间维度上进行管理呢?应该是可以的,比如CPU的多核和SMT/SMP设计,就是物理上配置了多组处理器或者流水线资源,实现空间维度的扩展。所以,运行LINUX系统的当代处理器,既支持时分,也支持空分管理,统统属于调度范畴。

那么内存在时间维度上管理的例子呢?或许LINUX系统中的内存交换机制可以看作内存在时间管理上的一个实现,为了在有限的内存提供尽量多的运行进程带宽,LINUX支持在内存紧张的时刻,将匿名页面交换到外部磁盘上,在需要的时候在再交换回来,实现对物理内存页面的分时复用。所以同CPU管理一样,LINUX系统对物理内存的管理也同时支持时分和空分。

CFS调度和优先级队列调度的区别

LINUX内核同时支持CFS调度和优先级队列调度,CFS调度算法用在选择SCHED_NORMAL策略的进程上,而内核支持的SCHED_RR实时调度策略则采用优先级队列的方式调度下一个要运行的进程。

至于这两种调度策略在内核中的区别,个人理解主要体现在对“优先级”的理解上。对于优先级队列这种方式来说,优先级的高低是选择下一个运行进程的唯一标准,在占有CPU这件事情上,高优先级的进程具有绝对的优先权,所以只要有高优先级的进程存在,低优先级进程永远没有执行的机会,所以会有线程饥饿情况的发生。

CFS调度策略对“优先级”的理解则不同,对于CFS调度策略来说,优先级只是代表进程在时间这个资源池中占据的“比重”或者“份数”,而并不是一种绝对的优先权。所以即便就绪队列中存在优先级很低的线程,但是仍然能够有一定的“比重”获取CPU。

所以,对于优先级队列来说,它的调度策略为:

next\_task = max(prio(A), prio(B), \cdots)

对于CFS调度算法来说,它的调度策略是选择距离执行到“预期比重”进度最慢的任务。需要有一种指标来衡量这种进度,同样的执行时间,进度增量和权重成反比,权重越大,进度增量应该越慢。

假设有两个进程的权重为W1,W2, W1 < W2, 选择W1作为其他任务的对照基准:

以两个权重分别为2和5的线程举例,按照CFS调度,其调度过程中进度变化如下表所示,进度最终得到相同。

所以,CFS调度算法保证的是,在持续调度的过程中,所有任务的执行进度和参照任务保持一致,其中参照任务是任意指定的,一般选择被大多数线程使用的任务权重作为参考基准,这样在计算过程中,比例引子为1,时间增量和进度增量相同,便于计算。

在LINUX内核调度器中,有一个专有名字表示进度指标,叫做虚拟时间。观察上表可以看到对于高优先级进程,由于其权重较大,因此它的虚拟时间是按照比例缩小的,也就是说,基准权重的时间和正常时间流逝相同,权重大于基准始终的任务,其时间流逝会变慢,CFS调度器会有限选择虚拟时间最慢的线程进行调度,所以高权重的任务才有更多的运行机会。对于权重低于基准权重的低优先级任务来说,前时间流逝会比正常的时间更快,这样,它只要执行较少的时间就可以满足对进度的要求,这样,在一个完整的调度周期内(队列中所有进程都得到一次调度的时间,比如上表中的W1+W2),每个任务都有执行的机会,进入尽量在调度周期结束后对齐虚拟时间。

之所以说是尽量,是因为内核中的调度点并不一定恰好在被权重整除的整数单位上,在一个DELTA TIME中,总会以有进程比预期多执行一会儿,获得了较多的虚拟时间增量,而其它进程少执行了一些。这就需要在下一个调度周期内进行补偿和奖励那些少运行的线程。因为调度周期是有限的,而CFS调度能够保证每个调度周期内每个任务都有机会执行,误差只会在不对齐的调度时刻出现,所以任务的虚拟时间总体上不会相差太大,CFS调度是一个不断追求公平的动态的过程,它实现了程序上的公平,并通过动态纠偏保证了结果的公平。

下面是参考内核CFS调度算法实现的算法模型仿真程序,分别使用红黑数和链表两种方式管理任务队列,支持动态添加任务:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <float.h>
#include <unistd.h>
#include <pthread.h>
#include <time.h>
#include <math.h>
#include "rbtree.h"#define CFS_USE_RB_TREE
// A CFS Scheduler CModel.
#define DBG(fmt, ...) do { printf("%s line %d, "fmt, __func__, __LINE__, ##__VA_ARGS__); } while (0)
#define assert(expr) \if (!(expr)) { \printf("Assertion failed! %s,%s,%s,line=%d\n",\#expr,__FILE__,__func__,__LINE__); \while(1); \}static pthread_mutex_t cfs_mutex;
double min_vruntime = 0.0f;
void update_min_vruntime(double vruntime)
{min_vruntime = vruntime;
}
/** Nice levels are multiplicative, with a gentle 10% change for every* nice level changed. I.e. when a CPU-bound task goes from nice 0 to* nice 1, it will get ~10% less CPU time than another CPU-bound task* that remained on nice 0.** The "10% effect" is relative and cumulative: from _any_ nice level,* if you go up 1 level, it's -10% CPU usage, if you go down 1 level* it's +10% CPU usage. (to achieve that we use a multiplier of 1.25.* If a task goes up by ~10% and another task goes down by ~10% then* the relative distance between them is ~25%.)*/
const double sched_prio_to_weight[40] = {
#if 1/* -20 */     88761,     71755,     56483,     46273,     36291,/* -15 */     29154,     23254,     18705,     14949,     11916,/* -10 */      9548,      7620,      6100,      4904,      3906,/*  -5 */      3121,      2501,      1991,      1586,      1277,/*   0 */      1024,       820,       655,       526,       423,/*   5 */       335,       272,       215,       172,       137,/*  10 */       110,        87,        70,        56,        45,/*  15 */        36,        29,        23,        18,        15,
#else/* -20 */     10,     10,     10,     10,     10,/* -15 */     10,     10,     10,     10,     10,/* -10 */     10,     10,     10,     10,     10,/*  -5 */     10,     10,     10,     10,     10,/*   0 */     10,     10,     10,     10,     10,/*   5 */     10,     10,     10,     10,     10,/*  10 */     10,     10,     10,     10,     10,/*  15 */     10,     10,     10,     10,     10,
#endif
};typedef struct sched_entity {struct rb_node node;int prio;int pid;double weight;double vruntime;double realtime;int ctx_switch;int on_rq;
} sched_entity_t;struct rb_root cfs_root_tree = RB_ROOT;
int sched_period(void)
{return rand() % 10 + 1;
}#define MAX_ENTRY 80
//#define SCHED_PERIOD 100
#define SCHED_PERIOD sched_period()
static sched_entity_t *tasks;// https://zhuanlan.zhihu.com/p/673572911
// in linux kernel, cfs_rq->load used for statistic the total
// weight in rq, refer update_load_add / update_load_sub.
double caculate_total_weight(void)
{double total = 0.0;
#if 1struct rb_node *node;sched_entity_t *task;for (node = rb_first(&cfs_root_tree); node; node = rb_next(node)) {task = rb_entry(node, sched_entity_t, node);total += task->weight;}
#elseint i;for (i = 0; i < MAX_ENTRY; i ++) {total += tasks[i].weight;}
#endifreturn total;
}double caculate_realtime(int task)
{double real = 0.0;real = SCHED_PERIOD * tasks[task].weight / caculate_total_weight();//printf("%s line %d, real %f.\n",  __func__, __LINE__, real);return real;
}double caculate_vruntime(sched_entity_t *task, int deltatime)
{double vruntime = 0.0;vruntime = deltatime * sched_prio_to_weight[20] / task->weight ;//printf("%s line %d, vruntime %f.\n",  __func__, __LINE__, vruntime);return vruntime;
}#define MAX_TICKS_TEST  100000000UL
double vruntime_total(unsigned long worldtime)
{return worldtime * sched_prio_to_weight[20] / caculate_total_weight();
}double realtime_total(sched_entity_t *task, unsigned long worldtime)
{
#if 1return worldtime * task->weight / caculate_total_weight();
#elsereturn vruntime_total(worldtime) * task->weight / sched_prio_to_weight[20];
#endif
}static int compare_prio(sched_entity_t *task1, sched_entity_t *task2)
{
#if 0if (task1->vruntime < task2->vruntime) {// task1 prior than task2.return -1;} else if (task1->vruntime > task2->vruntime) {// task2 prior than task1.return 1;} else {if (task1->weight > task2->weight) {return -1;} else {// task2 prior than task1.return 1;}}
#elsedouble res = (task1->vruntime == task2->vruntime) ? task1->weight - task2->weight : task2->vruntime - task1->vruntime;return res > 0.0f ? -1 : 1;
#endif
}static int cfs_rq_delete(struct rb_root *root, sched_entity_t *task)
{rb_erase(&task->node, root);return !RB_EMPTY_ROOT(root);
}static int cfs_rq_insert(struct rb_root *root,  sched_entity_t *task)
{int ret;struct rb_node **tmp = &(root->rb_node), *parent = NULL;/* Figure out where to put new node */while (*tmp) {sched_entity_t *this = rb_entry(*tmp, sched_entity_t, node);parent = *tmp;ret = compare_prio(task, this);if (ret < 0)tmp = &((*tmp)->rb_left);else if (ret > 0)tmp = &((*tmp)->rb_right);elsereturn -1;}/* Add new node and rebalance tree. */rb_link_node(&task->node, parent, tmp);rb_insert_color(&task->node, root);return 0;
}static void cfs_rq_destroy(struct rb_root *root)
{struct rb_node *node, *next;sched_entity_t *task;node = rb_first(root);while (node) {next = rb_next(node);task = rb_entry(node, sched_entity_t, node);rb_erase(node, root);node = next;}if (!RB_EMPTY_ROOT(root)) {printf("%s line %d, rb is not empty.\n", __func__, __LINE__);}return;
}void print_rbtree(struct rb_root *tree)
{struct rb_node *node;sched_entity_t *task;for (node = rb_first(tree); node; node = rb_next(node)) {task = rb_entry(node, sched_entity_t, node);printf("%s line %d, task(%d) prio %d,weight %f vruntime %f, on rq %d.\n",__func__, __LINE__, task->pid, task->prio, task->weight, task->vruntime, task->on_rq);}return;
}void init_cfs_rbtree(void)
{int i;for (i = 0; i < MAX_ENTRY; i ++) {tasks[i].on_rq = 1;cfs_rq_insert(&cfs_root_tree, &tasks[i]);}print_rbtree(&cfs_root_tree);return;
}#ifdef CFS_USE_RB_TREE
// O(logn) scheduler base on rbtree.
sched_entity_t *schedule(void)
{struct rb_node *node;sched_entity_t *task;node = rb_first(&cfs_root_tree);task = rb_entry(node, sched_entity_t, node);return task;
}#else
// A O(n) linear scheuler impl.
sched_entity_t *schedule(void)
{int i;int taskid = -1;double minruntime = DBL_MAX;// schedule policy:// 1.first find the task with the minum vruntime.// 2.if multiple task the the same minum vruntime, then// select the weighter one.for (i = 0; i < MAX_ENTRY; i ++) {if (minruntime > tasks[i].vruntime) {minruntime = tasks[i].vruntime;taskid = i;} else if (minruntime == tasks[i].vruntime) {if (tasks[i].weight > tasks[taskid].weight) {taskid = i;}}}return &tasks[taskid];
}
#endifdouble list_task_info(unsigned long worldtime)
{double total = 0.0;printf("==================================================================================================================================================================\n");
#if 1struct rb_node *node;sched_entity_t *task;for (node = rb_first(&cfs_root_tree); node; node = rb_next(node)) {task = rb_entry(node, sched_entity_t, node);total += task->realtime;printf("task(pid%d) vuntime %f, realtime %f, prio %d, weight %f, switches %d, ideal real time %f.\n",task->pid, task->vruntime, task->realtime, task->prio, task->weight, task->ctx_switch,realtime_total(task, worldtime));}
#elseint i;for (i = 0; i < MAX_ENTRY; i ++) {double ratio = 0.0;if (i > 0) {ratio = (tasks[i - 1].realtime - tasks[i].realtime) / tasks[i].realtime;}total += tasks[i].realtime;printf("task %d(pid%d) vuntime %f, realtime %f, prio %d, weight %f, incretio %f, switches %d, ideal real time %f.\n",i, tasks[i].pid, tasks[i].vruntime, tasks[i].realtime, tasks[i].prio, tasks[i].weight, ratio * 100, tasks[i].ctx_switch,realtime_total(&tasks[i], worldtime));}
#endifdouble staticis(void);printf("fangcha %f.\n", staticis());printf("==================================================================================================================================================================\n");return total;
}static void *fork_thread(void *arg)
{sched_entity_t *task;unsigned long *pworldtime = (unsigned long *)arg;static unsigned int pid = MAX_ENTRY;int i;while (1) {task = malloc(sizeof(sched_entity_t));memset(task, 0x00, sizeof(sched_entity_t));i = rand() % 40;pthread_mutex_lock(&cfs_mutex);task->prio = -20 + i;task->weight = sched_prio_to_weight[i];//task->vruntime = vruntime_total(*pworldtime) +1.5f;task->vruntime = min_vruntime + 1.5f;task->realtime = 0;task->ctx_switch = 0;task->pid = pid ++;task->on_rq = 0;cfs_rq_insert(&cfs_root_tree, task);task->on_rq = 1;pthread_mutex_unlock(&cfs_mutex);sleep(1);}return NULL;
}static void *exit_thread(void *arg)
{unsigned long *pwordtime = (unsigned long *)arg;while (1) {sleep(1);}return NULL;
}double staticis(void)
{double statics = 0.0f;double average = 0.0f;double total = 0.0f;int count = 0;struct rb_node *node;sched_entity_t *task;for (node = rb_first(&cfs_root_tree); node; node = rb_next(node)) {task = rb_entry(node, sched_entity_t, node);total += task->vruntime;count ++;}average = total / count;for (node = rb_first(&cfs_root_tree); node; node = rb_next(node)) {task = rb_entry(node, sched_entity_t, node);statics += (average - task->vruntime) * (average - task->vruntime);}return sqrt(statics / count);
}
int main(void)
{int i;unsigned long ticks;double total = 0.0;unsigned long worldtime = 0;pthread_t t1, t2;pthread_mutex_init(&cfs_mutex, NULL);tasks = malloc(sizeof(sched_entity_t) * MAX_ENTRY);memset(tasks, 0x00, sizeof(sched_entity_t) * MAX_ENTRY);if (!tasks) {printf("%s line %d, fatal errro, alloc failure.\n",__func__, __LINE__);return -1;}srand((unsigned int)time(0));for (i = 0; i < MAX_ENTRY; i ++) {tasks[i].prio = -20 + i % 40;tasks[i].weight = sched_prio_to_weight[i % 40];tasks[i].vruntime = 0;tasks[i].realtime = 0;tasks[i].ctx_switch = 0;tasks[i].pid = i;tasks[i].on_rq = 0;}#ifdef CFS_USE_RB_TREEinit_cfs_rbtree();
#endif// should be first.printf("%s line %d, first schedule select %ld.\n", __func__, __LINE__, schedule() - tasks);pthread_create(&t1, NULL, fork_thread, &worldtime);pthread_create(&t2, NULL, exit_thread, &worldtime);for (ticks = 0; /* ticks < MAX_TICKS_TEST */ 1; ticks ++) {double deltatime, vruntime;sched_entity_t *task;pthread_mutex_lock(&cfs_mutex);task = schedule();#ifdef CFS_USE_RB_TREEcfs_rq_delete(&cfs_root_tree, task);task->on_rq = 0;#endifdeltatime = SCHED_PERIOD;vruntime = caculate_vruntime(task, deltatime);task->vruntime += vruntime;task->realtime += deltatime;task->ctx_switch ++;worldtime += deltatime;update_min_vruntime(task->vruntime);#ifdef CFS_USE_RB_TREE// USE dequeue and enqueue to trigger the reorder of cfs rbtree ready queue.// this also the same in linux kernel, refer put_prev_entity(enqueue) and set_next_task(dequeue)// the only differenct is in linux kenrel the on_rq still keep no matter put_prev_entity/set_next_entity// involation.cfs_rq_insert(&cfs_root_tree, task);task->on_rq = 1;
#endifif (ticks % 1000 == 0) {list_task_info(worldtime);}pthread_mutex_unlock(&cfs_mutex);}pthread_mutex_lock(&cfs_mutex);total = list_task_info(worldtime);assert(total == worldtime);printf("vruntime %f.\n", vruntime_total(worldtime));pthread_mutex_unlock(&cfs_mutex);pthread_join(t1, NULL);pthread_join(t2, NULL);#ifdef CFS_USE_RB_TREEprint_rbtree(&cfs_root_tree);cfs_rq_destroy(&cfs_root_tree);
#endiffree(tasks);return 0;
}

仿真结果,按照CFS算法调度大约1000个进程,VRUNTIME的方差始终控制在70左右,下面截图中显示所有进程的VRUNTIME集中在[308177.014345,308736.000000],对照方差,VRUNTIME表示的所有任务的执行进度是非常集中的,说明CFS的调度策略确实照顾到了所有优先级的进程,使大家的执行进度基本保持在一个动态的一致范围之内。

参考文章

(五)Linux进程调度-CFS调度器

吐血整理 | 肝翻 Linux 进程调度所有知识点


结束

相关文章:

LINUX系统CFS调度模型实现思考和仿真

关于LINUX资源调度 计算机系统中&#xff0c;管理资源的方式一般有两种方法&#xff0c;分别是时间分割和空间分割&#xff0c;可以通过分割硬件的相似性&#xff0c;让软件以一致的逻辑执行&#xff0c;CPU运行特点是在时刻点A和时刻B运行机制是一样的&#xff0c;不同的只是…...

兑换码生成算法

兑换码生成算法 兑换码生成算法1.兑换码的需求2.算法分析2.重兑校验算法3.防刷校验算法 3.算法实现 兑换码生成算法 兑换码生成通常涉及在特定场景下为用户提供特定产品或服务的权益或礼品&#xff0c;典型的应用场景包括优惠券、礼品卡、会员权益等。 1.兑换码的需求 要求如…...

Vue框架介绍简介

Vue.js&#xff0c;通常简称为Vue&#xff0c;是一个用于构建用户界面的渐进式框架。它发布于2014年2月&#xff0c;由Evan You设计并开发。Vue被设计为可以自底向上逐层应用&#xff0c;这使得开发者可以根据项目的需求灵活地使用Vue。无论是构建简单的轻量级应用&#xff0c;…...

的C++奇迹之旅:值和引用的本质效率与性能比较

文章目录 请添加图片描述 [TOC](文章目录) &#x1f4dd;引用# &#x1f320;引用概念**引用**不是新定义一个变量&#xff0c;而是给**已存在变量取了一个别名**&#xff0c;编译器不会为引用变量开辟内存空间&#xff0c;它和它引用的变量共用同一块内存空间。>定义&#…...

【C++】vector问题解决(非法的间接寻址,迭代器失效 , memcpy拷贝问题)

送给大家一句话&#xff1a; 世界在旋转&#xff0c;我们跌跌撞撞前进&#xff0c;这就够了 —— 阿贝尔 加缪 vector问题解决 1 前言2 迭代器区间拷贝3 迭代器失效问题4 memcpy拷贝问题 1 前言 我们之前实现了手搓vector&#xff0c;但是当时依然有些问题没有解决&#xff…...

风控系统之普通规则条件,使用LiteFlow实现

个人博客&#xff1a;无奈何杨&#xff08;wnhyang&#xff09; 个人语雀&#xff1a;wnhyang 共享语雀&#xff1a;在线知识共享 Github&#xff1a;wnhyang - Overview 提要 参考&#xff1a;智能风控筑基手册&#xff1a;全面了解风控决策引擎 前面有可配置输入参数的接…...

在一套Dockerfile中完成编译和运行环境部署

大纲 解释型语言编译环境解释环境编译型语言编译环境运行环境 方法编译环境安装系统安装编译依赖下载代码特殊处理&#xff08;可以忽略&#xff09;编译准备&#xff08;可以忽略&#xff09;编译打包依赖&#xff08;编译结果&#xff09; 运行环境安装操作系统安装运行时依赖…...

ubuntu系统里克隆github代码到本地,提示fatal: unable to connect to github.com的解决方案

打开命令行终端生成一个新的SSH密钥对。如果你还没有SSH密钥或者想创建一个新的&#xff0c;可以使用以下命令&#xff1a; ssh-keygen -t rsa -b 4096 -C "your_emailexample.com"当系统提示你“Enter a file in which to save the key”&#xff0c;时&#xff0c;…...

常见docker使用命令

#搭建镜像 “”" sudo docker build -t es_refresh:V1.20230303 . “”" #启动容器 “”" docker run -d --namepara_classify -v /etc/localtime:/etc/localtime -v /data/chenhw/multi_label_classification:/edb2vec -p 8066:8066 --gpus ‘“device0”’…...

Ubuntu系统中设置中文输入法的教程

1、Ubuntu介绍&#xff1a; &#xff08;https://cn.ubuntu.com/&#xff09; &#xff08;Ubuntu | 全球领先的用于个人电脑、平板及手机的操作系统&#xff09; Ubuntu是一款基于Debian的开源Linux操作系统&#xff0c;由英国Canonical公司赞助支持的全球性社区共同开发。U…...

练习14 Web [极客大挑战 2019]Upload

phtml格式绕过&#xff0c;burp修改content-type绕过&#xff0c;常见的文件上传存放目录名 题目就叫upload&#xff0c;打开靶机 直接上传一个图片格式的一句话木马&#xff0c;返回如下&#xff1a; 提交练习5和9中的两种可以执行图片格式php代码的文件&#xff0c;修改con…...

3.6k star, 免费开源跨平台的数据库管理工具 dbgate

3.6k star, 免费开源跨平台的数据库管理工具 dbgate 分类 开源分享 项目名: dbgate -- 免费开源跨平台的数据库管理工具 Github 开源地址&#xff1a; GitHub - dbgate/dbgate: Database manager for MySQL, PostgreSQL, SQL Server, MongoDB, SQLite and others. Runs under…...

2024.3.2力扣每日一题——受限条件下可到达节点的数目

2024.3.2 题目来源我的题解方法一 深度优先搜索方法二 并查集 题目来源 力扣每日一题&#xff1b;题序&#xff1a;2368 我的题解 方法一 深度优先搜索 使用深度优先搜索实现&#xff0c;在搜索过程中根据restricted进行截停。 时间复杂度&#xff1a;O(n) 空间复杂度&#…...

在云端遇见雨云:一位服务器寻觅者的指南

引言&#xff1a;寻觅一座云端归宿 当我踏入数字世界的边缘&#xff0c;带着对网络的探索与期待&#xff0c;我迫切需要一座安全可靠的数字栖息地。云计算技术正如一场魔法般的变革&#xff0c;而在这片广袤的云端中&#xff0c;雨云就像是一位友善的向导&#xff0c;引领我穿越…...

Pygame基础10-物理模拟

PyMunk PyMunk是一个模拟物理的库。 注意&#xff0c;PyMunk只是进行物理模拟&#xff0c;不包含可视化的功能。如果需要可视化&#xff0c;可使用pygame等库。 可用pip安装pymunk pip install pymunk pymunk中的概念&#xff1a; space&#xff1a; 物理空间。 包含gravity 模…...

蓝桥杯 --- 日期问题模板

目录 1.如何判断闰年 2.如何遍历当前年份的每一天 3.如果想要输出某一年某一天到某一年某一天之间一共有多少天。 4.精确到具体周几到周几的问题分析 5.如何直接通过一层for循环枚举年月日 习题&#xff1a; 蓝桥杯竞赛特别喜欢考日期问题&#xff0c;今天给大家分享一下…...

Java 处理Mysql获取树形的数据

Mysql数据&#xff1a; 代码如下&#xff1a; Entity&#xff1a; Data Accessors(chain true) public class Region {private BigInteger id;//名称private String name;//父idprivate BigInteger parentId;private List<Region> children;private Integer createTim…...

前端三剑客 —— CSS ( 坐标问题 、定位问题和图片居中 )

前期内容回顾&#xff1a; 1.常见样式 text-shadow x轴 y轴 阴影的模糊程度 阴影的颜色 box-shadow border-radio 实现圆角 margin 内边距 padding 外边距 background 2.特殊样式 媒体查询&#xff1a;media 自定义字体&#xff1a;font-face { font-family:自定义名称&#…...

向量数据库 | AI时代的航道灯塔

向量数据库 | AI时代的航道灯塔 什么是向量检索服务拍照搜商品 你使用过向量数据库吗&#xff1f;使用体验&#xff1f;为什么向量数据库能借由大模型引起众多关注向量数据库在当前AI热潮中是昙花一现&#xff0c;还是未来AI时代的航道灯塔&#xff1f; 今天的话题主要是讨论向…...

Linux中的conntrack命令深入解析

在Linux网络管理和监控领域&#xff0c;conntrack命令是一个强大的工具&#xff0c;它提供了对netfilter连接跟踪系统的直接访问&#x1f50d;。这篇文章将深入探讨conntrack的由来、底层原理、参数意义&#xff0c;以及其常见用法&#xff0c;并对返回结果的每个字段进行详细解…...

wordpress后台更新后 前端没变化的解决方法

使用siteground主机的wordpress网站&#xff0c;会出现更新了网站内容和修改了php模板文件、js文件、css文件、图片文件后&#xff0c;网站没有变化的情况。 不熟悉siteground主机的新手&#xff0c;遇到这个问题&#xff0c;就很抓狂&#xff0c;明明是哪都没操作错误&#x…...

【Python】 -- 趣味代码 - 小恐龙游戏

文章目录 文章目录 00 小恐龙游戏程序设计框架代码结构和功能游戏流程总结01 小恐龙游戏程序设计02 百度网盘地址00 小恐龙游戏程序设计框架 这段代码是一个基于 Pygame 的简易跑酷游戏的完整实现,玩家控制一个角色(龙)躲避障碍物(仙人掌和乌鸦)。以下是代码的详细介绍:…...

相机Camera日志实例分析之二:相机Camx【专业模式开启直方图拍照】单帧流程日志详解

【关注我&#xff0c;后续持续新增专题博文&#xff0c;谢谢&#xff01;&#xff01;&#xff01;】 上一篇我们讲了&#xff1a; 这一篇我们开始讲&#xff1a; 目录 一、场景操作步骤 二、日志基础关键字分级如下 三、场景日志如下&#xff1a; 一、场景操作步骤 操作步…...

第一篇:Agent2Agent (A2A) 协议——协作式人工智能的黎明

AI 领域的快速发展正在催生一个新时代&#xff0c;智能代理&#xff08;agents&#xff09;不再是孤立的个体&#xff0c;而是能够像一个数字团队一样协作。然而&#xff0c;当前 AI 生态系统的碎片化阻碍了这一愿景的实现&#xff0c;导致了“AI 巴别塔问题”——不同代理之间…...

C++.OpenGL (10/64)基础光照(Basic Lighting)

基础光照(Basic Lighting) 冯氏光照模型(Phong Lighting Model) #mermaid-svg-GLdskXwWINxNGHso {font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}#mermaid-svg-GLdskXwWINxNGHso .error-icon{fill:#552222;}#mermaid-svg-GLd…...

12.找到字符串中所有字母异位词

&#x1f9e0; 题目解析 题目描述&#xff1a; 给定两个字符串 s 和 p&#xff0c;找出 s 中所有 p 的字母异位词的起始索引。 返回的答案以数组形式表示。 字母异位词定义&#xff1a; 若两个字符串包含的字符种类和出现次数完全相同&#xff0c;顺序无所谓&#xff0c;则互为…...

AI编程--插件对比分析:CodeRider、GitHub Copilot及其他

AI编程插件对比分析&#xff1a;CodeRider、GitHub Copilot及其他 随着人工智能技术的快速发展&#xff0c;AI编程插件已成为提升开发者生产力的重要工具。CodeRider和GitHub Copilot作为市场上的领先者&#xff0c;分别以其独特的特性和生态系统吸引了大量开发者。本文将从功…...

MySQL中【正则表达式】用法

MySQL 中正则表达式通过 REGEXP 或 RLIKE 操作符实现&#xff08;两者等价&#xff09;&#xff0c;用于在 WHERE 子句中进行复杂的字符串模式匹配。以下是核心用法和示例&#xff1a; 一、基础语法 SELECT column_name FROM table_name WHERE column_name REGEXP pattern; …...

使用 SymPy 进行向量和矩阵的高级操作

在科学计算和工程领域&#xff0c;向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能&#xff0c;能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作&#xff0c;并通过具体…...

【C++进阶篇】智能指针

C内存管理终极指南&#xff1a;智能指针从入门到源码剖析 一. 智能指针1.1 auto_ptr1.2 unique_ptr1.3 shared_ptr1.4 make_shared 二. 原理三. shared_ptr循环引用问题三. 线程安全问题四. 内存泄漏4.1 什么是内存泄漏4.2 危害4.3 避免内存泄漏 五. 最后 一. 智能指针 智能指…...