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

高并发内存池(四):Page Cache结构设计

目录

一,项目整体框架回顾

 Thread Cache结构

Central Cache结构

二,Page Cache大致框架

三,Page Cache申请内存实现

Central Cache向Page Cache申请内存接口

从Page Cache中获取span接口

Page Cache加锁问题

申请内存完整过程

源码:


一,项目整体框架回顾

首先,项目整体可以划分为三层:Thread Cache,Central Cache和Page Cache.

 Thread Cache结构

第一层thread cache是每个线程独有的,每个线程都有一份自己的thread cache对象,来完成自己的申请内存和释放内存的工作。

结构特点:

thread cache是一个类似于哈希桶的结构,每个位置上挂着的都是一些小块内存,这些小块内存是以自由链表的形式组织在一起的。当申请内存空间时,都是从对应的位置(对应的哈希桶)中头删一个内存块,返回给上层使用。

为了使每个线程只看到自己的thread cache的对象,用到了线程局部存储机制。所谓线程局部存储,本质上是一种存储变量 的方法,这个变量在它所在的线程内可以访问,其他线程访问不到。这样就实现了每个线程独享一个thread cache

申请内存:

由于每个线程独享一个thread cache,所以可以直接找thread cache申请内存,不需要加锁。这里就会涉及到thread cache的内存对齐规则,当需要申请的内存大小是n时,我们需要计算出其对应的两个值:实际申请的内存块大小(对齐数),对应在哪个桶中(也就是哪个位置)。

如果对应的桶中还有小块内存,也就是对应的桶不为空时,直接返回一个 内存块给上层。

如果对应的桶中没有内存块,或者已经分配完了,则此时就需要向下一层Central Cache申请。

Central Cache结构

Central Cache为thread Cache提供服务。当thread cache内存不够时,会向Central Cache申请。

所以Central Cache是需要加锁的,但是不是给整体的Central Cache加锁,而是对某个桶加锁。

结构特点:

Central Cache结构与thread cache结构很像,也是哈希桶结构.只不过thread cache管理的是小块内存,而Central Cache管理的内存块更大,在这里称作是span。

一个span,管理的是以页为单位的内存块,一页按照8KB来计算。不同位置上的span大小不一样。

所以Central Cache每个位置上挂的是一个一个的span,span的大小是按照页来计算的。

为thread cache分配内存:

由于thread cache和Central Cache的内存对齐规则是一样的,当thread cahce对应位置上没有小块内存时,就会来找Central Cache申请,申请的位置是一样的。这也就是为什么要将Central Cache设计的和thread cahce一样,申请内存时,直接找相同的位置即可。

而Central Cache每个位置上都是span结构,总不能把整个span返回给thread cache吧。

所以,在span内部,是将一个大的内存块,切分成小内存块,然后使用自由链表管理起来。

所以,总体申请内存的过程如下:

thread cahce对应的位置没有内存了,会像Central Cache申请,会来到相同的位置。这里会存在多个线程并发访问的问题,需要加锁,也就是对当前位置(桶)加锁即可,不需要对整个Central Cache加锁。

然后查看这个位置上的span链表,找到一个有内存块的span,也就是非空的span。从中切分一些返回给thread cache,然后将第一个内存块返回该上层使用,剩下的插入到thread cache对应位置中。

下次再申请时,直接找thread cache,因为无锁,申请高高效。

但是,如果Central Cache对应位置的内存也是用完了,此时就需要像下一层Page Cache申请

二,Page Cache大致框架

Page Cache是为上层Central Cache提供服务的,当 Central Cache对应位置没有内存时,就需要像Page Cache申请。

而Central Cache每个位置上挂的是一个span结构,在给 Central Cache分配内存的时候,返回的应该也是一个span结构。所以Page Cache每个位置上挂的也是一个一个的span。

和Central Cache一样,Page Cache每个位置上都是span链表,大致结构如下:

如上图所示,Page Cache也是一个类似于哈希桶的设计。它与thread cache和Central Cache不同 ,它们两个还需要采用什么麻烦的内存对齐规则,才能找到对应的位置(桶)。

Page Cache相当于是直接定址法的哈希结构,1页就映射1号桶,2页就映射2号桶......

因为上层最多一层申请256KB。而128页位置的span大小是128*8KB,所以分为128页够用了。 

如果Central Cache申请的span大小是n页的,就去n号位置找(具体的分配过程在代码部分讲解)

三,Page Cache申请内存实现

Page Cache也是所有的线程共享的,和Central Cache一样,需要设计成单例模式。

//和Central Cache一样,设计成单例模式
class PageCache
{
public:PageCache() = delete;PageCache(const PageCache&) = delete;PageCache operator=(const PageCache&) = delete;//获取单例对象static PageCache* GetInstance(){return &_sInst;}//给Central Cache分配一个span//参数size表示该span的大小,即该span包含多少个页span* NewSpan(size_t size);private:SpanList _pageList[NPAGENUM];static PageCache _sInst;
};

Central Cache向Page Cache申请内存接口

在上一篇文章中,实现了thread cahce向Central Cache申请内存的接口。

大致思路是:

先找到对应Central Cache中的位置,然后遍历该位置,找一个非空的span,然后将给span中的一些内存块切分出来,第一块返回给上层使用,剩下的让thread cache保存着。

在代码部分,获取一个非空的span时,只是调用了对应的接口,没有完全实现,因为 这部分和Page Cache有关。

现在我们需要实现这个接口,也就是获取到对应位置的一个非空span。

上图是Central Cache的大致结构,要找一个非空的span,就需要遍历整个 spanlist。

如果找到一个非空的span,就直接返回该span。

如果找不到,也就是当前位置为空,或者所有的span都已经分配出去了,此时就需要向一层Page Cache申请对应大小的span,我们可以直接调用Page Cache提供的接口(在后面实现)。

  • thread cache内存不足时,Central Cache会分配更多的内存给thread cache。
  • 同样的,Central Cahce对应位置的span都为空时,Page Cache也会分配更多的内存给Central Cache。
  • 所以,此时我们需要计算分配多大的span合适,也就是span包含多少个页。在thread cache向Central Cache申请内存的时候,使用的是慢开始反馈调节算法,每次分配的个数呈现出慢增长的趋势。
  • 我们知道现在想要申请的内存块大小是size。我们可以计算出Central Cache给thread cache分配的最大个数n,就是上限个数(也就是在满开始反馈调节算法中使用到的)
  • 然后我们就可以计算出需要分配的总字节数n*size。
  • 最后看看n*size包含多少个页,采用向上对齐。size_t num=n*size/8KB,这样就可以计算出页的大小。

当从Page Cache申请到一个span后,这个 span是一个大块内存。我们需要进行切分,切分成小块内存,使用自由链表管理起来。

 

完整代码如下: 

//thread chache向Central Cache申请内存时,获取的内存块的个数的上限
static size_t NumMoveSize(size_t size)
{assert(size > 0);//MAX_BYTES是256KBint num = MAX_BYTES / size;//size较大时,num的结果可能是1,num=2,多分配一个if (num < 2)num = 2;//size较小是,num的结果可能很大,甚至上万个//512相当于一个上限if (num >= 512)num = 512;return num;
}	
//现在知道要申请的内存块大小是size,计算分配多大的span合适//也就是计算span,是管理多少页的static size_t NumPageSize(size_t size){//首先计算,thread cache向Central Cache申请时的上限个数size_t num = NumMoveSize(size);//计算总的字节数size_t byte = num * size;byte >>= PAGE_SHIFT;//除以 8KB,计算出页数if (byte == 0)byte = 1;return byte;}
//从对应的哈希桶,也就是spanlist中,获取一个非空的span
span* CentralCache::GetOneSpan(SpanList& list, size_t size)
{//list就是对应的哈希桶//从这个桶中查找一个非空的span,返回//如果这个桶没有span了,则需要向下层 Page Cache申请//遍历这个spanlist,spanlist是带头双向循环链表结构auto it = list.Begin();while (it != list.End()){if (it != nullptr){//找到非空的span,也就是Central Cache层还有span,直接返回return it;}else{it = it->_next;}}//走到这,说明该spanlist已经没有空闲的span了,需要向下层申请//这里就会面临向下层申请多大的span合适//太大,可能浪费太多,太小,可能会导致频繁的申请//所以,这里和thread cache向Central Cache申请内存类似,需要计算一个合适的值//参数代表申请的span的大小span* sp = PageCache::GetInstance()->NewSpan(SizeClass::NumPageSize(size));assert(sp);//从Page Cache获取到一个span后,将该span进行切分//切成小块内存,形成自由链表,小块内存的大小就是申请时候的size//计算这个span总的大小//页数*一页的大小(8KB)size_t bytes = sp->_n << PAGE_SHIFT;//根据sp的起始页号,推断出这个sp的起始地址//一页是8KB,第几个页乘以8KB即可推导出起始地址char* start = (char*)(sp->_pageID << PAGE_SHIFT);char* end = start + bytes;//end指向整个span的结尾//切分,每一个内存块的大小是size并以自由链表的形式组织在一起//先将第一块切下来,尾插到自由链表中sp->_freelist = start;start += size;//进行尾插void* tail = sp->_freelist;while (start < end){NextObj(tail) = start;//尾插一个starttail = start;//tail中心指向尾start += size;}//sp的结构已经设置好了,现在将它分配给Central Cache//将它插入到对应的哈希桶中,也就是参数中的listlist.pushFront(sp);//至此,返回的span,就是一个包含小块内存的结构return sp;
}

接下来,就需要实现Page Cache的NewSpan接口了。

从Page Cache中获取span接口

	//参数k表示该span的页数,即该span包含多少个页span* NewSpan(size_t k);

从Page Cache中获取一个span,span的大小为k页。

而在上面的内容中,我们了解到Page Cache是一个直接定址法的哈希桶。

所以想申请大小为size页的span,直接去对应size位置的桶找即可。

  1. 但这里与前面不同,thread cache对应位置为空时,会找下一层申请,Central Cache为空时,也会找下一层申请。但是如果Page Cache对应某个位置为空时,他会去下面的位置继续找,直到找到一个非空的。比如,当访问Page Cache的3号桶 为空时,会继续找比他更大的桶,假设7号桶非空,那么此时就会将7号桶切成3页和4页,然后将4页挂到4号桶上,3页返回给Central Cache。
  2. 但是如果整个Page Cache都为空,那么此时就会像下一层申请,也就是像操作系统申请一大块的空间。

从一个大小为n页的span中,切分出大小为k的span。

可以总结为以下3步:假设申请的span为k页。

  1. 先去Page Cache对应的位置(下标为k)查找是否有span。如果有 ,直接返回该span。
  2. 如果没有,去找更大的span,从下标为k+1开始遍历整个Page Cache,如果找到了一个非空span,将该span进行切分,一部分返回,另一部分重新挂到Page Cache对应的位置上。
  3. 如果遍历Page Cache也没有找到一个非空的span,就需要向操作系统申请。一次申请128页大小的空间。

还有一个问题,就是如果向操作系统申请了一大块空间,如何将这一大块空间挂到Page Cache对应的位置。本质就是将这一大块空间使用span管理起来。然后将span挂到对应的位置上即可。

向操作系统申请内存,返回的是这块内存的起始地址,是一个指针类型。需要根据返回的地址,计算出这 个大块内存对应的起始页号。如下图:

完整代码如下:

//win64中包含了win32和win64的定义
//win32中只包含了win32的定义
#ifdef _WIN64
typedef unsigned long long  PAGE_ID;
#elif _WIN32
typedef size_t PAGE_ID;
#endif//申请的内存上限
static const size_t  MAX_BYTES = 256 * 1024;
//自由链表的个数,也就是哈希桶的个数
static const size_t  NFREELISTS = 208;
//Page Cache哈希桶的个数,下标从1开始
static const size_t  NPAGENUM = 129;//一页的大小(指数)
static const size_t  PAGE_SHIFT = 13;// 直接去堆上申请空间
inline static void* SystemAlloc(size_t kpage)
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage * (1 << 12), MEM_COMMIT | MEM_RESERVE,PAGE_READWRITE);
#else// linux下调用brk mmap等
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}//给Central Cache分配一个span
//参数k表示该span的大小,即该span包含多少个页
span* PageCache::NewSpan(size_t k)
{//查看下标为k的位置是否有spanif (!_pageList[k].Empty()){//直接返回return _pageList[k].PopFront();}//遍历下面的位置,切分出大小为k页for (int i = k + 1; i < NPAGENUM; i++){if (!_pageList[i].Empty()){//找到一个非空的,开始切分span* kspan = new span;span* nspan = _pageList[i].PopFront();//拿到一个spankspan->_pageID = nspan->_pageID;kspan->_n = k;nspan->_pageID += k;nspan->_n -= k;//将n-k页的span,头插到n-k的桶中_pageList[nspan->_n].pushFront(nspan);return kspan;}}//走到这里说明没有大块的span了,需要找操作系统申请//直接要一个128页的spanspan* bigspan = new span;void* ptr = SystemAlloc(NPAGENUM - 1);//128页span* sp = new span;sp->_pageID = (PAGE_ID)ptr >> (PAGE_SHIFT);//起始页号sp->_n = NPAGENUM - 1;//页的个数//从操作系统申请到内存后,再递归调用自己一次获取spanreturn NewSpan(k);
}

Page Cache加锁问题

Page Cache也是所有线程共享的,在高并发环境下,会存在线程安全问题,所以是需要加锁的。

在Central Cache中,使用的是桶锁,每个线程申请内存时,到对应位置的桶去申请内存,所以加的是桶锁。

Page Cache也是哈希桶结构,当然也可以设计成桶锁,每个桶有一把锁。但是通过上面申请内存的过程,我们有可能不是只针对某一个桶,而是去遍历下面所有的桶,所以如果设计成桶锁的结构,就会面临频繁的申请锁和释放锁,导致系统频繁的进行保存上下文和切换上下文的操作,效率太低。

所以在向Page Cache申请内存时,需要将整个结构加锁。

申请内存完整过程

每个线程启动时,首先向对应的thread cache申请内存,不需要加锁,如果有内存,直接返回给上层使用。

如果不存在,会向Central Cache申请,需要加锁,使用的是桶锁,Central Cache会多分配几个给thread cache。

如果Central Cache也为空,此时会找到一下层Page Cache,需要对整个结构进行加锁。

注意:在Central Cache层使用桶锁后,如果来到下一层Page Cache,就需要将该锁解开。

因为有可能会有其他线程向Central Cache归还内存。

源码:

ConcurrentMemoryPool · 小鬼/高并发内存池 - 码云 - 开源中国

相关文章:

高并发内存池(四):Page Cache结构设计

目录 一&#xff0c;项目整体框架回顾 Thread Cache结构 Central Cache结构 二&#xff0c;Page Cache大致框架 三&#xff0c;Page Cache申请内存实现 Central Cache向Page Cache申请内存接口 从Page Cache中获取span接口 Page Cache加锁问题 申请内存完整过程 源码&a…...

易学探索助手-项目记录(九)

本文介绍本地大模型推理数据集构成 &#xff08;一&#xff09;古籍数据获取 以44种竖向从右至左排列的繁体古文为研究对象&#xff0c;通过OCR识别、XML结构化处理&#xff0c;最终生成符合大模型训练要求的数据集。 1.技术路线设计 图像处理层&#xff1a;PaddleOCR识别竖…...

Idea 设置编码UTF-8 Idea中 .properties 配置文件中文乱码

Idea 设置编码UTF-8 Idea中 .properties 配置文件中文乱码 一、设置编码 1、步骤&#xff1a; File -> Setting -> Editor -> File encodings --> 设置编码二、配置文件中文乱码 1、步骤&#xff1a; File -> Setting -> Editor -> File encodings ->…...

Redis缓存穿透、雪崩、击穿的解决方案?

Redis 缓存问题解决方案及Java实现 一、缓存穿透解决方案 &#xff08;缓存穿透指查询不存在数据&#xff0c;绕过缓存直接访问数据库&#xff09; 1. 布隆过滤器 空值缓存 注意点&#xff1a; 1.布隆过滤器是需要预热数据的&#xff0c;就是需要输入当前数据库已经存在的…...

第29节:现代CNN架构-Inception系列模型

引言 Inception系列模型是卷积神经网络(CNN)发展历程中的重要里程碑,由Google研究人员提出并不断演进。这一系列模型通过创新的架构设计,在保持计算效率的同时显著提升了图像识别任务的性能。从最初的Inception v1到最新的Inception-ResNet,每一代Inception模型都引入了突破…...

初识C++:类和对象(上)

概述&#xff1a;本篇博客主要讲解类和对象的学习。 目录 1. 类的定义 1.1 类定义格式 1.2 访问限定符 1.3 类域 2.实例化 2.1 实例化概念 2.2 this指针 3. 小结 1. 类的定义 1.1 类定义格式 class为定义类的关键字&#xff0c;Stack为类的名字&#xff0c;{} 中为类的…...

腾讯 IMA 工作台升级:新增知识库广场与 @提问功能

目录 一、引言 二、知识库广场功能 2.1 功能架构解析 2.2 技术实现突破 三、知识库提问功能 3.1 交互模式革新 3.2 技术底层逻辑 四、实战价值 4.1 知识管理方面 4.2 工作效率提升方面 4.3 团队协作方面 4.4 知识变现方面 五、未来展望 5.1 技术演进方向 5.2 商业…...

[目标检测] YOLO系列算法讲解

前言 目标检测就是做到给模型输入一张图片或者视频&#xff0c;模型可以迅速判断出视频和图片里面感兴趣的目标所有的位置和它 的类别&#xff0c;而当前最热门的目标检测的模型也就是YOLO系列了。 YOLO系列的模型的提出&#xff0c;是为了解决当时目标检测的模型帧率太低而提…...

Python 之 selenium 打开浏览器指定端口进行接续操作

一般使用 selenium 进行数据爬取时&#xff0c;常用处理流程是让 selenium 从打开浏览器开始&#xff0c;完成全流程的所有操作。但是有时候&#xff0c;我们希望用户先自己打开浏览器进入指定网页&#xff0c;完成登录认证等一系列操作之后&#xff08;比如用户、密码、短信验…...

GPUGeek携手ComfyUI :低成本文生图的高效解决方案

一、文生图领域的困境与ComfyUI的优势 在当今数字化创意表达的时代&#xff0c;文生图技术日益受到关注。像豆包这类以语言交互为主的大模型&#xff0c;虽然在文本处理上表现出色&#xff0c;但在文生图方面&#xff0c;其生成效果难以达到专业图像创作的要求。而Midjourney&…...

OpenCV CUDA 模块中用于在 GPU 上计算两个数组对应元素差值的绝对值函数absdiff(

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 void cv::cuda::absdiff 是 OpenCV CUDA 模块中的一个函数&#xff0c;用于在 GPU 上计算两个数组对应元素差值的绝对值。 该函数会逐元素计算两…...

互联网大厂Java面试题:深入解析SpringCloud微服务架构中的服务注册与发现机制

互联网大厂Java面试题&#xff1a;深入解析SpringCloud微服务架构中的服务注册与发现机制 面试题 问题&#xff1a; 在SpringCloud微服务架构中&#xff0c;服务注册与发现是核心功能之一。请详细说明Nacos作为服务注册中心的实现原理&#xff0c;并对比其与Eureka的异同点。…...

东芝新四款产品“TB67Z830SFTG、TB67Z830HFTG、TB67Z850SFTG、TB67Z850HFTG系列三相栅极驱动器ic三相栅极驱动器IC

支持用于电动工具、吸尘器、工业机器人等的三相无刷DC电机 东芝电子设备与存储公司(简称“东芝”)推出了TB67Z83xxFTG系列三相栅极驱动器ic的四个产品和TB67Z85xxFTG系列三相栅极驱动器IC的四个产品&#xff0c;用于消费和工业设备的三相无刷DC电机。通过将新产品与作为电机控制…...

react+html-docx-js将页面导出为docx

1.主要使用&#xff1a;html-docx-js进行前端导出 2.只兼容到word&#xff0c;wps兼容不太好 3.处理分页换行 4.处理页眉 index.tsx import { saveAs } from file-saver; import htmlToDocxGenerate from ./HtmlToDocx;const handleExportByHtml async () > {const expor…...

Linux基础 -- SSH 流式烧录与压缩传输笔记

Linux SSH 流式烧录与压缩传输指南 一、背景介绍 在嵌入式开发和维护中&#xff0c;常常需要通过 SSH 从 PC 向设备端传输大文件&#xff08;如系统镜像、固件&#xff09;并将其直接烧录到指定磁盘&#xff08;如 /dev/mmcblk2&#xff09;。然而&#xff0c;设备端存储空间…...

<论文>(微软)避免推荐域外物品:基于LLM的受限生成式推荐

一、摘要 本文介绍微软和深圳大学合作发表于2025年5月的论文《Avoid Recommending Out-of-Domain Items: Constrained Generative Recommendation with LLMs》。论文主要研究如何解决大语言模型&#xff08;LLMs&#xff09;在推荐系统中推荐域外物品的问题&#xff0c;提出了 …...

2025年渗透测试面试题总结-360[实习]安全工程师(题目+回答)

网络安全领域各种资源&#xff0c;学习文档&#xff0c;以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具&#xff0c;欢迎关注。 目录 1. 自我介绍 2. WAF及其绕过方式 3. IPS/IDS/HIDS 4. 云安全 5. 绕过安骑士/安全狗 6. Gopher扩展…...

【机器学习】支持向量回归(SVR)从入门到实战:原理、实现与优化指南

前言 在机器学习的广阔领域中&#xff0c;回归分析作为预测连续型变量的重要手段&#xff0c;被广泛应用于金融预测、工业生产、科学研究等诸多场景。支持向量回归&#xff08;SVR&#xff09;作为回归算法家族中的佼佼者&#xff0c;凭借独特的理论优势与强大的实践能力脱颖而…...

右值引用的学习

传统的C语法中就有引用的语法&#xff0c;而C11中新增了的右值引用语法特性&#xff0c;所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用&#xff0c;都是给对象取别名。 左值引用和右值引用 在讲之前&#xff0c;我们先来看一下什么是左值和右值…...

碎片笔记|AI生成图像溯源方法源码复现经验(持续更新中……)

前言&#xff1a;本篇博客分享一些溯源方法的复现经验&#xff0c;希望能帮助到大家&#x1f389;。 目录 1. Close-set AttributionRepmixDe-FakeDNA-Net 2. Open-set AttributionPOSE 3. Single-Model AttributionOCC-CLIPLatentTracer 1. Close-set Attribution Repmix 论…...

elementplus el-tree 二次封装支持配置删除后展示展开或折叠编辑复选框懒加载功能

本文介绍了基于 ElementPlus 的 el-tree 组件进行二次封装的 TreeView 组件&#xff0c;使用 Vue3 和 JavaScript 实现。TreeView 组件通过 props 接收树形数据、配置项等&#xff0c;支持懒加载、节点展开/收起、节点点击、删除、编辑等操作。组件内部通过 ref 管理树实例&…...

【C/C++】深度探索c++对象模型_笔记

1. 对象内存布局 (1) 普通类&#xff08;无虚函数&#xff09; 成员变量排列&#xff1a;按声明顺序存储&#xff0c;但编译器会根据内存对齐规则插入填充字节&#xff08;padding&#xff09;。class Simple {char a; // 1字节&#xff08;偏移0&#xff09;int b; …...

Spring MVC数据绑定和响应 你了解多少?

数据绑定的概念 在程序运行时&#xff0c;Spring MVC接收到客户端的请求后&#xff0c;会根据客户端请求的参数和请求头等数据信息&#xff0c;将参数以特定的方式转换并绑定到处理器的形参中。Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑…...

外贸礼品禁忌

一、亚洲 1.印度 牛是神圣动物&#xff0c;别送牛皮制品。另外&#xff0c;左手不洁&#xff0c;送礼得用右手或双手。 2.日本 “梳” 和 “苦” 谐音&#xff0c;不送梳子。日本男性不咋佩戴首饰&#xff0c;除结婚戒指。礼物得装盒、纸包、绳饰&#xff0c;白色包装得有…...

【MySQL 基础篇】深入解析MySQL逻辑架构与查询执行流程

1 MySQL逻辑架构概述 MySQL 的逻辑架构主要分为 Server 层和存储引擎层两部分。 Server 层&#xff1a;包含连接器、查询缓存、分析器、优化器、执行器等组件。同时&#xff0c;所有的内置函数&#xff08;如日期、时间、数学和加密函数等&#xff09;也在这一层实现。此外&a…...

基于C#实现中央定位服务器的 P2P 网络聊天系统

基于中央定位服务器的 P2P 网络聊天系统 1 需求分析与实现功能 本次作业旨在实现一个基于中央定位服务器的 P2P 网络聊天系统&#xff0c;也即通过中央定位服务器实现登入&#xff0c;登出与好友的 IP 查询等操作&#xff0c;在好友间的通信使用 P2P 来完成&#xff0c;具体见…...

【C++】map和set的模拟实现

1.底层红黑树节点的定义 enum Colur {RED,BLACK }; template<class T> struct RBTreeNode {RBTreeNode<T>* _left;RBTreeNode<T>* _right;RBTreeNode<T>* _parent;T _data;Colur _col;RBTreeNode(const T& data):_left(nullptr), _right(nullptr…...

数据结构·字典树

字典树trie 顾名思义&#xff0c;在一个字符串的集合里查询某个字符串是否存在树形结构。 树存储方式上用的是结构体数组&#xff0c;类似满二叉树的形式。 模板 定义结构体和trie 结构体必须的内容&#xff1a;当前结点的字符&#xff0c;孩子数组可选&#xff1a;end用于查…...

centos服务器,疑似感染phishing家族钓鱼软件的检查

如果怀疑 CentOS 服务器感染了 Phishing 家族钓鱼软件&#xff0c;需要立即进行全面检查并采取相应措施。以下是详细的检查和处理步骤&#xff1a; 1. 立即隔离服务器 如果可能&#xff0c;将服务器从网络中隔离&#xff0c;以防止进一步传播或数据泄露。如果无法完全隔离&…...

(C语言)超市管理系统(测试2版)(指针)(数据结构)(清屏操作)

目录 前言&#xff1a; 源代码&#xff1a; product.h product.c fileio.h fileio.c main.c 代码解析&#xff1a; 一、程序结构概述 二、product.c 函数详解 1. 初始化商品列表 Init_products 2. 添加商品 add_product 3. 显示商品 display_products 4. 修改商品 mo…...

可变形卷积简介(Deformable Convolution)

1. 核心原理 可变形卷积通过动态调整卷积核的采样位置&#xff0c;增强模型对几何形变&#xff08;如旋转、缩放&#xff09;的适应能力。其核心改进包括&#xff1a; 偏移量&#xff08;Offset&#xff09;&#xff1a;为卷积核的每个采样点学习 x / y x/y x/y方向的偏移量 …...

02_Servlet

目录 一、简介二、Servlet入门案例2.1、编写Servlet2.2、配置Servlet2.3、访问Servlet2.4、常见错误 三、Servlet详解3.1、实现Servlet的三种方式3.1.1、实现Servlet接口3.1.2、继承GenericServlet类3.1.3、继承HttpServlet类 3.2、配置Servlet的两种方式3.2.1、web.xml方式3.2…...

stm32之FLASH

目录 1.简介2.闪存模块组织3.基本结构3.1 FPEC3.2 程序存储器3.2.1 标准编程3.2.2 页擦除3.2.3 全擦除 3.3 选项字节3.3.1 编程3.3.2 擦除 4.器件电子签名5.实验-读取内部FLASH 1.简介 STM32F1系列的FLASH内存是一个非常重要的存储区域&#xff0c;它主要由三个部分组成&#…...

第3.4节 调用链路分析服务开发

3.4.1 什么是Code Call Graph&#xff08;CCG&#xff09; Code Call Graph&#xff08;CCG&#xff09;即业务代码中的调用关系图&#xff0c;是通过静态分析手段分析并构建出的一种描述代码间关系的图。根据精度不同&#xff0c;一般分为类级别、方法级别、控制流级别&#x…...

超详细Docker教程

前言&#xff1a;大家在在Linux上部署mysql及其他软件时&#xff0c;大家想一想自己最大的感受是什么&#xff1f; 我相信&#xff0c;除了个别天赋异禀的人以外&#xff0c;大多数人都会有相同的感受&#xff0c;那就是麻烦。核心体现在三点&#xff1a; 命令太多了&#xff…...

探索AI新领域:生成式人工智能认证(GAI认证)助力职场发展

在数字化时代的大潮中&#xff0c;人工智能&#xff08;AI&#xff09;技术以其强大的影响力和广泛的应用前景&#xff0c;正逐步重塑我们的生活与工作方式。随着生成式AI技术的崛起&#xff0c;掌握这一前沿技能已成为职场竞争中的关键优势。那么&#xff0c;如何通过系统的学…...

sql sql复习

虽然之前学习过sql&#xff0c;但由于重在赶学习进度&#xff0c;没有学扎实&#xff0c;导致自己刷题的时候有的地方还是模模糊糊&#xff0c;现在主要是复习&#xff0c;补一补知识点。 今日靶场&#xff1a; NSSCTF 云曦历年考核题 在做题之前先回顾一下sql注入的原理&…...

初探 Skynet:轻量级分布式游戏服务器框架实战

在游戏服务器开发领域&#xff0c;高效、稳定且易于扩展的框架一直是开发者追求的目标。Skynet 作为一款轻量级、高性能的分布式游戏服务器框架&#xff0c;凭借其独特的设计理念和强大的功能&#xff0c;赢得了众多开发者的青睐 一.Skynet底层架构支持 1.Actor erlang 从语言…...

libarchive.so.19丢失

文章目录 绝对路径可以启动&#xff0c;相对路径不可以以绝对路径启动conda环境&#xff0c;运行python3.8(成功) 报错 Error while loading conda entry point: conda-libmamba-solver (libarchive.so.19: cannot open shared object file: No such file or directory) sudo a…...

vue-ganttastic甘特图label标签横向滚动固定方法

这个甘特图之前插件里&#xff0c;没有找到能固定label标签在屏幕上的办法&#xff0c;用css各种办法都没有实现&#xff0c;所以我我直接手写定位&#xff0c;用js监听滚动条滚动的距离&#xff0c;然后同步移动甘特图label标签&#xff0c;造成一种定位的错觉&#xff0c;以下…...

自动化 NuGet 包打包与上传:完整批处理脚本详解(含 SVN 支持)

在大型项目中&#xff0c;我们常常需要定期打包多个 .csproj 项目为 NuGet 包&#xff0c;并上传到私有 NuGet 服务。这篇文章分享一份实战脚本&#xff0c;支持以下自动化流程&#xff1a; 自动读取、更新 .csproj 文件中的 Version、PackageOutputPath 等节点&#xff1b; 自…...

Go语言空白导入的作用与用途

在 Go 语言中&#xff0c;导入包时前面加下划线 _ 是一种特殊的导入方式&#xff0c;称为 “空白导入” 或 “匿名导入”。 作用&#xff1a; 执行包的初始化&#xff08;init 函数&#xff09;但不直接使用包中的标识符 import _ "go.uber.org/automaxprocs" 表示你…...

实验六:按键模拟控制实现

FPGA序列检测器实验(远程实验系统) 文章目录 FPGA序列检测器实验(远程实验系统)一、数字电路基础知识1. 时钟与同步2. 按键消抖原理代码讲解:分频与消抖3. 有限状态机(FSM)设计代码讲解:状态机编码与转移4. 边沿检测与信号同步5. 模块化设计二、实验数字电路整体思想三…...

【愚公系列】《Manus极简入门》038-数字孪生设计师:“虚实映射师”

&#x1f31f;【技术大咖愚公搬代码&#xff1a;全栈专家的成长之路&#xff0c;你关注的宝藏博主在这里&#xff01;】&#x1f31f; &#x1f4e3;开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主&#xff01; &#x1f…...

Linux重定向与缓冲区

目录 文件描述符的分配规则 重定向 使用 dup2 系统调用 FILE 文件描述符的分配规则 #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h>int main() {int fd open("myfile", O_RDONLY);if(fd < 0){per…...

经典还原反应解析:Wolff-Kishner机制与黄鸣龙改进法

在有机化学发展史上记载的万余种经典反应中&#xff0c;当论及以科学家命名的标志性转化反应时&#xff0c;Wolff-Kishner-黄鸣龙还原反应必然占据重要地位。在大学《有机化学》课程中&#xff0c;学习还原反应时肯定会提到Wolff-Kishner-黄鸣龙还原反应&#xff0c;这是第一个…...

DataX从Mysql导数据到Hive分区表案例

0、下载DataX并解压到对应目录 DataX安装包&#xff0c;开箱即用&#xff0c;无需配置。 https://datax-opensource.oss-cn-hangzhou.aliyuncs.com/202308/datax.tar.gz 相关参考文档 https://github.com/alibaba/DataX/blob/master/hdfswriter/doc/hdfswriter.md 1、Hive分区…...

npm 报错 gyp verb `which` failed Error: not found: python2 解决方案

一、背景 npm 安装依赖报如下错&#xff1a; gyp verb check python checking for Python executable "python2" in the PATH gyp verb which failed Error: not found: python2 一眼看过去都觉得是Python环境问题&#xff0c;其实并不是你python环境问题&#xf…...

安装npm:npm未随Node.js一起安装

文章目录 上传至linux服务器/usr/local/目录下 如果npm没有随Node.js一起安装&#xff0c;你可以尝试单独下载并安装npm。但通常情况下&#xff0c;这是不必要的&#xff0c;因为npm是Node.js的一部分。如果确实需要单独安装npm&#xff0c;你可以参考npm的官方安装指南。 npm…...

C++23 ranges::to:范围转换函数 (P1206R7)

文章目录 引言C23 Ranges 概述ranges::to 的定义与功能定义功能 使用场景范围转换为容器简化字符串解析映射转换为向量 ranges::to 的优势代码简洁性提高开发效率与C23的stl容器的范围版本构造函数配合良好 模板参数约束的思考总结 引言 在C的发展历程中&#xff0c;每一个新版…...