高并发内存池(四):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位置的桶找即可。
- 但这里与前面不同,thread cache对应位置为空时,会找下一层申请,Central Cache为空时,也会找下一层申请。但是如果Page Cache对应某个位置为空时,他会去下面的位置继续找,直到找到一个非空的。比如,当访问Page Cache的3号桶 为空时,会继续找比他更大的桶,假设7号桶非空,那么此时就会将7号桶切成3页和4页,然后将4页挂到4号桶上,3页返回给Central Cache。
- 但是如果整个Page Cache都为空,那么此时就会像下一层申请,也就是像操作系统申请一大块的空间。
从一个大小为n页的span中,切分出大小为k的span。
可以总结为以下3步:假设申请的span为k页。
- 先去Page Cache对应的位置(下标为k)查找是否有span。如果有 ,直接返回该span。
- 如果没有,去找更大的span,从下标为k+1开始遍历整个Page Cache,如果找到了一个非空span,将该span进行切分,一部分返回,另一部分重新挂到Page Cache对应的位置上。
- 如果遍历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结构设计
目录 一,项目整体框架回顾 Thread Cache结构 Central Cache结构 二,Page Cache大致框架 三,Page Cache申请内存实现 Central Cache向Page Cache申请内存接口 从Page Cache中获取span接口 Page Cache加锁问题 申请内存完整过程 源码&a…...
易学探索助手-项目记录(九)
本文介绍本地大模型推理数据集构成 (一)古籍数据获取 以44种竖向从右至左排列的繁体古文为研究对象,通过OCR识别、XML结构化处理,最终生成符合大模型训练要求的数据集。 1.技术路线设计 图像处理层:PaddleOCR识别竖…...
Idea 设置编码UTF-8 Idea中 .properties 配置文件中文乱码
Idea 设置编码UTF-8 Idea中 .properties 配置文件中文乱码 一、设置编码 1、步骤: File -> Setting -> Editor -> File encodings --> 设置编码二、配置文件中文乱码 1、步骤: File -> Setting -> Editor -> File encodings ->…...
Redis缓存穿透、雪崩、击穿的解决方案?
Redis 缓存问题解决方案及Java实现 一、缓存穿透解决方案 (缓存穿透指查询不存在数据,绕过缓存直接访问数据库) 1. 布隆过滤器 空值缓存 注意点: 1.布隆过滤器是需要预热数据的,就是需要输入当前数据库已经存在的…...
第29节:现代CNN架构-Inception系列模型
引言 Inception系列模型是卷积神经网络(CNN)发展历程中的重要里程碑,由Google研究人员提出并不断演进。这一系列模型通过创新的架构设计,在保持计算效率的同时显著提升了图像识别任务的性能。从最初的Inception v1到最新的Inception-ResNet,每一代Inception模型都引入了突破…...
初识C++:类和对象(上)
概述:本篇博客主要讲解类和对象的学习。 目录 1. 类的定义 1.1 类定义格式 1.2 访问限定符 1.3 类域 2.实例化 2.1 实例化概念 2.2 this指针 3. 小结 1. 类的定义 1.1 类定义格式 class为定义类的关键字,Stack为类的名字,{} 中为类的…...
腾讯 IMA 工作台升级:新增知识库广场与 @提问功能
目录 一、引言 二、知识库广场功能 2.1 功能架构解析 2.2 技术实现突破 三、知识库提问功能 3.1 交互模式革新 3.2 技术底层逻辑 四、实战价值 4.1 知识管理方面 4.2 工作效率提升方面 4.3 团队协作方面 4.4 知识变现方面 五、未来展望 5.1 技术演进方向 5.2 商业…...
[目标检测] YOLO系列算法讲解
前言 目标检测就是做到给模型输入一张图片或者视频,模型可以迅速判断出视频和图片里面感兴趣的目标所有的位置和它 的类别,而当前最热门的目标检测的模型也就是YOLO系列了。 YOLO系列的模型的提出,是为了解决当时目标检测的模型帧率太低而提…...
Python 之 selenium 打开浏览器指定端口进行接续操作
一般使用 selenium 进行数据爬取时,常用处理流程是让 selenium 从打开浏览器开始,完成全流程的所有操作。但是有时候,我们希望用户先自己打开浏览器进入指定网页,完成登录认证等一系列操作之后(比如用户、密码、短信验…...
GPUGeek携手ComfyUI :低成本文生图的高效解决方案
一、文生图领域的困境与ComfyUI的优势 在当今数字化创意表达的时代,文生图技术日益受到关注。像豆包这类以语言交互为主的大模型,虽然在文本处理上表现出色,但在文生图方面,其生成效果难以达到专业图像创作的要求。而Midjourney&…...
OpenCV CUDA 模块中用于在 GPU 上计算两个数组对应元素差值的绝对值函数absdiff(
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 void cv::cuda::absdiff 是 OpenCV CUDA 模块中的一个函数,用于在 GPU 上计算两个数组对应元素差值的绝对值。 该函数会逐元素计算两…...
互联网大厂Java面试题:深入解析SpringCloud微服务架构中的服务注册与发现机制
互联网大厂Java面试题:深入解析SpringCloud微服务架构中的服务注册与发现机制 面试题 问题: 在SpringCloud微服务架构中,服务注册与发现是核心功能之一。请详细说明Nacos作为服务注册中心的实现原理,并对比其与Eureka的异同点。…...
东芝新四款产品“TB67Z830SFTG、TB67Z830HFTG、TB67Z850SFTG、TB67Z850HFTG系列三相栅极驱动器ic三相栅极驱动器IC
支持用于电动工具、吸尘器、工业机器人等的三相无刷DC电机 东芝电子设备与存储公司(简称“东芝”)推出了TB67Z83xxFTG系列三相栅极驱动器ic的四个产品和TB67Z85xxFTG系列三相栅极驱动器IC的四个产品,用于消费和工业设备的三相无刷DC电机。通过将新产品与作为电机控制…...
react+html-docx-js将页面导出为docx
1.主要使用:html-docx-js进行前端导出 2.只兼容到word,wps兼容不太好 3.处理分页换行 4.处理页眉 index.tsx import { saveAs } from file-saver; import htmlToDocxGenerate from ./HtmlToDocx;const handleExportByHtml async () > {const expor…...
Linux基础 -- SSH 流式烧录与压缩传输笔记
Linux SSH 流式烧录与压缩传输指南 一、背景介绍 在嵌入式开发和维护中,常常需要通过 SSH 从 PC 向设备端传输大文件(如系统镜像、固件)并将其直接烧录到指定磁盘(如 /dev/mmcblk2)。然而,设备端存储空间…...
<论文>(微软)避免推荐域外物品:基于LLM的受限生成式推荐
一、摘要 本文介绍微软和深圳大学合作发表于2025年5月的论文《Avoid Recommending Out-of-Domain Items: Constrained Generative Recommendation with LLMs》。论文主要研究如何解决大语言模型(LLMs)在推荐系统中推荐域外物品的问题,提出了 …...
2025年渗透测试面试题总结-360[实习]安全工程师(题目+回答)
网络安全领域各种资源,学习文档,以及工具分享、前沿信息分享、POC、EXP分享。不定期分享各种好玩的项目及好用的工具,欢迎关注。 目录 1. 自我介绍 2. WAF及其绕过方式 3. IPS/IDS/HIDS 4. 云安全 5. 绕过安骑士/安全狗 6. Gopher扩展…...
【机器学习】支持向量回归(SVR)从入门到实战:原理、实现与优化指南
前言 在机器学习的广阔领域中,回归分析作为预测连续型变量的重要手段,被广泛应用于金融预测、工业生产、科学研究等诸多场景。支持向量回归(SVR)作为回归算法家族中的佼佼者,凭借独特的理论优势与强大的实践能力脱颖而…...
右值引用的学习
传统的C语法中就有引用的语法,而C11中新增了的右值引用语法特性,所以从现在开始我们之前学习的引用就叫做左值引用。无论左值引用还是右值引用,都是给对象取别名。 左值引用和右值引用 在讲之前,我们先来看一下什么是左值和右值…...
碎片笔记|AI生成图像溯源方法源码复现经验(持续更新中……)
前言:本篇博客分享一些溯源方法的复现经验,希望能帮助到大家🎉。 目录 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 组件,使用 Vue3 和 JavaScript 实现。TreeView 组件通过 props 接收树形数据、配置项等,支持懒加载、节点展开/收起、节点点击、删除、编辑等操作。组件内部通过 ref 管理树实例&…...
【C/C++】深度探索c++对象模型_笔记
1. 对象内存布局 (1) 普通类(无虚函数) 成员变量排列:按声明顺序存储,但编译器会根据内存对齐规则插入填充字节(padding)。class Simple {char a; // 1字节(偏移0)int b; …...
Spring MVC数据绑定和响应 你了解多少?
数据绑定的概念 在程序运行时,Spring MVC接收到客户端的请求后,会根据客户端请求的参数和请求头等数据信息,将参数以特定的方式转换并绑定到处理器的形参中。Spring MVC中将请求消息数据与处理器的形参建立连接的过程就是Spring MVC的数据绑…...
外贸礼品禁忌
一、亚洲 1.印度 牛是神圣动物,别送牛皮制品。另外,左手不洁,送礼得用右手或双手。 2.日本 “梳” 和 “苦” 谐音,不送梳子。日本男性不咋佩戴首饰,除结婚戒指。礼物得装盒、纸包、绳饰,白色包装得有…...
【MySQL 基础篇】深入解析MySQL逻辑架构与查询执行流程
1 MySQL逻辑架构概述 MySQL 的逻辑架构主要分为 Server 层和存储引擎层两部分。 Server 层:包含连接器、查询缓存、分析器、优化器、执行器等组件。同时,所有的内置函数(如日期、时间、数学和加密函数等)也在这一层实现。此外&a…...
基于C#实现中央定位服务器的 P2P 网络聊天系统
基于中央定位服务器的 P2P 网络聊天系统 1 需求分析与实现功能 本次作业旨在实现一个基于中央定位服务器的 P2P 网络聊天系统,也即通过中央定位服务器实现登入,登出与好友的 IP 查询等操作,在好友间的通信使用 P2P 来完成,具体见…...
【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 顾名思义,在一个字符串的集合里查询某个字符串是否存在树形结构。 树存储方式上用的是结构体数组,类似满二叉树的形式。 模板 定义结构体和trie 结构体必须的内容:当前结点的字符,孩子数组可选:end用于查…...
centos服务器,疑似感染phishing家族钓鱼软件的检查
如果怀疑 CentOS 服务器感染了 Phishing 家族钓鱼软件,需要立即进行全面检查并采取相应措施。以下是详细的检查和处理步骤: 1. 立即隔离服务器 如果可能,将服务器从网络中隔离,以防止进一步传播或数据泄露。如果无法完全隔离&…...
(C语言)超市管理系统(测试2版)(指针)(数据结构)(清屏操作)
目录 前言: 源代码: product.h product.c fileio.h fileio.c main.c 代码解析: 一、程序结构概述 二、product.c 函数详解 1. 初始化商品列表 Init_products 2. 添加商品 add_product 3. 显示商品 display_products 4. 修改商品 mo…...
可变形卷积简介(Deformable Convolution)
1. 核心原理 可变形卷积通过动态调整卷积核的采样位置,增强模型对几何形变(如旋转、缩放)的适应能力。其核心改进包括: 偏移量(Offset):为卷积核的每个采样点学习 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内存是一个非常重要的存储区域,它主要由三个部分组成&#…...
第3.4节 调用链路分析服务开发
3.4.1 什么是Code Call Graph(CCG) Code Call Graph(CCG)即业务代码中的调用关系图,是通过静态分析手段分析并构建出的一种描述代码间关系的图。根据精度不同,一般分为类级别、方法级别、控制流级别&#x…...
超详细Docker教程
前言:大家在在Linux上部署mysql及其他软件时,大家想一想自己最大的感受是什么? 我相信,除了个别天赋异禀的人以外,大多数人都会有相同的感受,那就是麻烦。核心体现在三点: 命令太多了ÿ…...
探索AI新领域:生成式人工智能认证(GAI认证)助力职场发展
在数字化时代的大潮中,人工智能(AI)技术以其强大的影响力和广泛的应用前景,正逐步重塑我们的生活与工作方式。随着生成式AI技术的崛起,掌握这一前沿技能已成为职场竞争中的关键优势。那么,如何通过系统的学…...
sql sql复习
虽然之前学习过sql,但由于重在赶学习进度,没有学扎实,导致自己刷题的时候有的地方还是模模糊糊,现在主要是复习,补一补知识点。 今日靶场: NSSCTF 云曦历年考核题 在做题之前先回顾一下sql注入的原理&…...
初探 Skynet:轻量级分布式游戏服务器框架实战
在游戏服务器开发领域,高效、稳定且易于扩展的框架一直是开发者追求的目标。Skynet 作为一款轻量级、高性能的分布式游戏服务器框架,凭借其独特的设计理念和强大的功能,赢得了众多开发者的青睐 一.Skynet底层架构支持 1.Actor erlang 从语言…...
libarchive.so.19丢失
文章目录 绝对路径可以启动,相对路径不可以以绝对路径启动conda环境,运行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标签横向滚动固定方法
这个甘特图之前插件里,没有找到能固定label标签在屏幕上的办法,用css各种办法都没有实现,所以我我直接手写定位,用js监听滚动条滚动的距离,然后同步移动甘特图label标签,造成一种定位的错觉,以下…...
自动化 NuGet 包打包与上传:完整批处理脚本详解(含 SVN 支持)
在大型项目中,我们常常需要定期打包多个 .csproj 项目为 NuGet 包,并上传到私有 NuGet 服务。这篇文章分享一份实战脚本,支持以下自动化流程: 自动读取、更新 .csproj 文件中的 Version、PackageOutputPath 等节点; 自…...
Go语言空白导入的作用与用途
在 Go 语言中,导入包时前面加下划线 _ 是一种特殊的导入方式,称为 “空白导入” 或 “匿名导入”。 作用: 执行包的初始化(init 函数)但不直接使用包中的标识符 import _ "go.uber.org/automaxprocs" 表示你…...
实验六:按键模拟控制实现
FPGA序列检测器实验(远程实验系统) 文章目录 FPGA序列检测器实验(远程实验系统)一、数字电路基础知识1. 时钟与同步2. 按键消抖原理代码讲解:分频与消抖3. 有限状态机(FSM)设计代码讲解:状态机编码与转移4. 边沿检测与信号同步5. 模块化设计二、实验数字电路整体思想三…...
【愚公系列】《Manus极简入门》038-数字孪生设计师:“虚实映射师”
🌟【技术大咖愚公搬代码:全栈专家的成长之路,你关注的宝藏博主在这里!】🌟 📣开发者圈持续输出高质量干货的"愚公精神"践行者——全网百万开发者都在追更的顶级技术博主! …...
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机制与黄鸣龙改进法
在有机化学发展史上记载的万余种经典反应中,当论及以科学家命名的标志性转化反应时,Wolff-Kishner-黄鸣龙还原反应必然占据重要地位。在大学《有机化学》课程中,学习还原反应时肯定会提到Wolff-Kishner-黄鸣龙还原反应,这是第一个…...
DataX从Mysql导数据到Hive分区表案例
0、下载DataX并解压到对应目录 DataX安装包,开箱即用,无需配置。 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 安装依赖报如下错: gyp verb check python checking for Python executable "python2" in the PATH gyp verb which failed Error: not found: python2 一眼看过去都觉得是Python环境问题,其实并不是你python环境问题…...
安装npm:npm未随Node.js一起安装
文章目录 上传至linux服务器/usr/local/目录下 如果npm没有随Node.js一起安装,你可以尝试单独下载并安装npm。但通常情况下,这是不必要的,因为npm是Node.js的一部分。如果确实需要单独安装npm,你可以参考npm的官方安装指南。 npm…...
C++23 ranges::to:范围转换函数 (P1206R7)
文章目录 引言C23 Ranges 概述ranges::to 的定义与功能定义功能 使用场景范围转换为容器简化字符串解析映射转换为向量 ranges::to 的优势代码简洁性提高开发效率与C23的stl容器的范围版本构造函数配合良好 模板参数约束的思考总结 引言 在C的发展历程中,每一个新版…...