【项目日记(七)】第三层: 页缓存的具体实现(上)
💓博主CSDN主页:杭电码农-NEO💓
⏩专栏分类:项目日记-高并发内存池⏪
🚚代码仓库:NEO的学习日记🚚
🌹关注我🫵带你做项目
🔝🔝
开发环境:Visual Studio 2022
项目日记
- 1. 前言
- 2. 页缓存的具体结构
- 3. 页缓存分配内存的全过程
- 4. 页缓存分配内存的代码实现
- 5. 优化代码,并完全脱离malloc
- 6. 总结以及代码拓展
1. 前言
在页缓存这一层中,负责给中心缓存
分配大块儿的内存,以及合并前后空
闲的内存,这一层为整体加锁!
本章重点:
本篇文章着重讲解内存池第三层:
页缓存的基本成员变量和函数,以
页缓存的具体结构是怎样的.了解
完基础结构后,会详解讲解中心缓存
层来申请内存时的具体步骤!
2. 页缓存的具体结构
页缓存也是一个哈希桶结构,但它的映射
规则和前两层不同,它的规则是:
K号桶中的大块儿内存就是K页
并且它总共是有128号桶,申请小于
128页的内存都会在内存池中申请
//单例模式
class PageCache
{
public:static PageCache* GetInstance(){return &_singleton;}//获取一个K页的spanSpanData* NewSpan(size_t k);std::mutex _mtx;//pagecache不能用桶锁,只能用全局锁,因为后面可能会有大页被切割为小页// 获取从对象到span的映射,给我一个地址,返回这个地址对应的spanSpanData* MapObjectToSpan(void* obj);// 释放空闲span回到Pagecache,并合并相邻的spanvoid ReleaseSpanToPageCache(SpanData* span);
private:PageCache(){}PageCache(const PageCache& obj) = delete;
private:std::unordered_map<PAGE_ID, SpanData*> _idSpanMap;//存储页号和桶中对应的span的映射,解决换回来的内存对应哪个span的问题SpanList _spanList[N_PAGES];static PageCache _singleton;
};
3. 页缓存分配内存的全过程
当中心缓存中没有内存时,会去页缓存
申请一个span结构,要经过下面几步:
- 根据中心缓存的桶号来确定申请的span是几页的
- 根据中心缓存想要申请的页数,找到页缓存中对应的桶(k页对应k号桶)
- 情况一: 页缓存的K号桶中存在span结构,直接将这块儿内存返回给中心缓存
- 情况二: 页缓存的K号桶没有span结构,但是K+1到128号桶中存在span结构,假设n号桶有span,则将这个大页的span切分为一个k页的span和一个n-k页的span,k页的span返回给中心缓存去使用,而将n-k页的span重新挂在n-k号桶中
- 情况三: k到128号桶都没有span,此时页缓存会向系统申请一份128页大小的内存,并挂在128号桶中,再将这个128页的span切分为k页的span和128-k页的span,也就转换为了情况二!
并且在这个过程中,页缓存将一个span
分配给中心缓存后,会记录下来这块儿
内存的页号和span的映射关系,方便后续
回收内存的时候再使用!
4. 页缓存分配内存的代码实现
在pagecache.h文件中:
SpanData* PageCache::NewSpan(size_t k)//去第K个桶中找span给central,此i号桶中挂的span都是i页内存
{//若K桶中有,直接返回,K桶没有span就往后找去分裂大spanassert(k > 0);if (k > N_PAGES - 1)//如果申请的页数大于了128页,pagecache只能向堆申请了{void* ptr = SystemAlloc(k);SpanData* span = new SpanData();span->_pageid = (PAGE_ID)ptr >> PAGE_SHIFT;span->_n = k;_idSpanMap[span->_pageid] = span;return span;}//先检查K号桶有无span,有直接返回一个if (!_spanList[k].Empty()){SpanData* KSpan = _spanList[k].PopFront();for (PAGE_ID i = 0; i < KSpan->_n; i++)_idSpanMap[KSpan->_pageid + i] = KSpan;return KSpan;}//走到这儿代表k号桶为空,检查后面的桶有没有span,拿出来分裂成两个小spanfor (int i = k + 1; i < N_PAGES; i++){if (!_spanList[i].Empty())//k页的span返回给centralcache,i-k页的span挂到i-k号桶中{SpanData* ISpan = _spanList[i].PopFront();SpanData* KSpan = new SpanData;KSpan->_pageid = ISpan->_pageid;KSpan->_n = k;ISpan->_pageid += k;//把头K页切分给KSpanISpan->_n -= k; //页数从i变为i-k_spanList[ISpan->_n].PushFront(ISpan);//再将后i-k页分配给i-k号桶//存储Ispan的首尾页号跟ISpan的映射关系// 这里只需要映射首尾页而不需要像下面一样全部页都映射,因为下面切分出去的span会被切分为小块儿内存// 这些小块儿内存都有可能被使用,所以当它们还回来时这些小块儿内存可能映射的是不同的页,但这些页都属于这个KSpan// 然而ISpan中不会被切分为小块儿内存,它只需要关心是否和它的前后页合并,所以这里只需要映射首尾页号与ISpan的关系// ISpan作为要合并页的前面,如1000页要合并ISpan是1001页,那么1001到1001+n都是空闲的!ISpan作为要合并页的后面,如100页要合并ISpan是999页,那么999-n都是空闲的!//_idSpanMap[ISpan->_pageid] = ISpan;//_idSpanMap[ISpan->_pageid + ISpan->_n - 1] = ISpan;_idSpanMap.set(ISpan->_pageid, ISpan);_idSpanMap.set(ISpan->_pageid + ISpan->_n - 1, ISpan);//建立id和span的映射关系,方便centralcache回收小块内存时查看哪块内存在哪块spanfor (PAGE_ID i = 0; i < KSpan->_n; i++)//返回的KSpan中一共有n页,并且每一页的页号都对应KSpan这个地址_idSpanMap[KSpan->_pageid + i] = KSpan;return KSpan;}}//走到这里说明后面所有的桶都没有span了//这时需要向堆申请一个128页的span再拿来做切分SpanData* bigSpan = _spanPool.New();void* ptr = SystemAlloc(N_PAGES - 1);bigSpan->_pageid = (PAGE_ID)ptr >> PAGE_SHIFT;bigSpan->_n = N_PAGES - 1;_spanList[bigSpan->_n].PushFront(bigSpan);//将这个128页的span插入到桶中return NewSpan(k);//再次调用自己,这次一定会在前面的for循环处返回
}
这个地方有一个设计的比较巧妙的点,
那就是出现情况三的时候,向系统申请了
128页的空间后,再次调用这个函数就一定
会出现情况二,从而在for循环中走完整个过程
5. 优化代码,并完全脱离malloc
细心的同学会发现,在这个函数中使用到了new操作符,然而了解new底层原理的同学应该知道,new的底层实际上是用的malloc来申请的空间,但是我们这个项目就是为了完全脱离malloc函数来实现一个多线程下高效的内存池,所以这里一定不能使用new!
使用之前编写的定长池来舍弃new!
如果你不知道或忘记了定长池是什么
请看这篇文章: 定长池的实现
首先, 在页缓存类中添加上一个成员变量: 定长池类, 然后在使用new的地方,把new全部替换为用定长池申请空间!
6. 总结以及代码拓展
页缓存分配内存的一环设计的是
非常的巧妙,但是页缓存真正巧妙
的地方是在合并空闲内存的一环!
对代码的拓展:
我们会发现页缓存结构中调用了
好几次向系统申请内存的函数,
这个地方只做了解,会用接口就行
inline static void* SystemAlloc(size_t kpage)//申请kpage页内存
{
#ifdef _WIN32void* ptr = VirtualAlloc(0, kpage << PAGE_SHIFT, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else// linux下brk mmap等直接向系统申请内存的方式
#endifif (ptr == nullptr)throw std::bad_alloc();return ptr;
}
相关文章:

【项目日记(七)】第三层: 页缓存的具体实现(上)
💓博主CSDN主页:杭电码农-NEO💓 ⏩专栏分类:项目日记-高并发内存池⏪ 🚚代码仓库:NEO的学习日记🚚 🌹关注我🫵带你做项目 🔝🔝 开发环境: Visual Studio 2022 项目日…...

深入解剖指针篇(2)
目录 指针的使用 strlen的模拟实现 传值调用和传址调用 数组名的理解 使用指针访问数组 一维数组传参的本质 冒泡排序 个人主页(找往期文章):我要学编程(ಥ_ಥ)-CSDN博客 指针的使用 strlen的模拟实现 库函数strlen的功能是求字符串…...

【知识点】Java常用
文章目录 基础基础数据类型内部类Java IOIO多路复用重要概念 Channel **通道**重要概念 Buffer **数据缓存区**重要概念 Selector **选择器** 关键字final 元注解常用接口异常处理ErrorException JVM与虚拟机JVM内存模型本地方法栈虚拟机栈 Stack堆 Heap方法区 Method Area (JD…...

MySQL篇—迁移数据目录到新的本地路径
☘️博主介绍☘️: ✨又是一天没白过,我是奈斯,DBA一名✨ ✌✌️擅长Oracle、MySQL、SQLserver、Linux,也在积极的扩展IT方向的其他知识面✌✌️ ❣️❣️❣️大佬们都喜欢静静的看文章,并且也会默默的点赞收藏加关注❣…...

【FPGA】高云FPGA之IP核的使用->PLL锁相环
FPGA开发流程 1、设计定义2、设计输入3、分析和综合4、功能仿真5、布局布线6、时序仿真7、IO分配以及配置文件(bit流文件)的生成8、配置(烧录)FPGA9、在线调试 1、设计定义 使用高云内置IP核实现多路不同时钟输出 输入时钟50M由晶…...

程控设备和电脑通信的总线和协议选择
文章目录 程控设备都通过什么协议和总线和电脑通信?工控设备都使用什么通信协议与电脑通信?各种工控设备通信协议的优缺点如何选择适合工控设备的通信协议?各种工控设备通信总线的优缺点如何判断一种总线是否适合特定的应用场景?程控设备都通过什么协议和总线和电脑通信? …...

鸿蒙开发-UI-组件导航-Navigation
鸿蒙开发-UI-组件 鸿蒙开发-UI-组件2 鸿蒙开发-UI-组件3 鸿蒙开发-UI-气泡/菜单 鸿蒙开发-UI-页面路由 文章目录 目录 一、基本概念 二、页面显示模式 1.自适应模式 2.单页面模式 3.分栏模式 三、标题栏模式 1.Mini模式 2.Full模式 四、菜单栏 五、工具栏 六、案例 …...

Codeforces Round 922 (Div. 2)(A~D)补题
A题考虑贪心,要使使用的砖头越多,每块转的k应尽可能小,最小取2,最后可能多出来,多出来的就是最后一块k3,我们一行内用到的砖头就是 m 2 \frac{m}{2} 2m下取整,然后乘以行数就是答案。 #inclu…...

Seata下载与配置、启动
目录 Seata下载Seata配置启动Seata Seata下载 首先,我们需要知道我们要使用哪个版本的seata,这就要查看spring-cloud-alibaba版本说明,找到我们对应的seata。 spring-cloud-alibaba版本说明: 地址链接 下面是部分版本说明: s…...

什么是TCP三次握手、四次挥手?
1、三次握手 你(客户端)给一个朋友(服务器)打电话,告诉他你想开始对话。这就像是发送一个SYN(同步序列编号)信号,表示你想开始建立连接。(client向server发送syn,seqx,此时client验证client发送能力正常。client置为SYN SENT状态)…...

C++程序在开机自启和定时器执行时遇到的问题和解决方法
遇到的错误如下: Camera is created.load vfvlog.[dll/so] failed for dll[/vfvlog.so] unexistedLoadDbgConfig, LoadFile fail, err:-3, errno: No such file or directoryqt.qpa.xcb: could not connect to displayqt.qpa.plugin: Could not load the Qt platfo…...

R17 extended DRX(eDRX)
根据工业无线传感器的要求,电池至少可持续使用数年。 在NB-IoT和LTE eMTC中,也有类似的要求。 为了满足极长电池寿命的要求,NB-IoT和LTE eMTC在Release 13中为RRC IDLE UE引入了扩展DRX,在Release 16中为RRC INACTIVE UE引入了eDRX,上面是LTE 引入eDRX的背景。 一 概述 到…...

Debezium发布历史102
原文地址: https://debezium.io/blog/2021/02/24/debezium-1-5-beta1-released/ 欢迎关注留言,我是收集整理小能手,工具翻译,仅供参考,笔芯笔芯. Debezium 1.5.0.Beta1 发布 2021 年 2 月 24 日 作者: Gu…...

探索自然语言处理在改善搜索引擎、语音助手和机器翻译中的应用
文章目录 每日一句正能量前言文本分析语音识别机器翻译语义分析自然语言生成情感分析后记 每日一句正能量 努力学习,勤奋工作,让青春更加光彩。 前言 自然语言处理(NLP)是人工智能领域中与人类语言相关的重要研究方向,…...

echarts:获取省、市、区/县、镇的地图数据
目录 第一章 前言 第二章 获取地图的数据(GeoJSON格式) 2.1 获取省、市、区/县地图数据 2.2 获取乡/镇/街道地图数据 第一章 前言 需求:接到要做大屏的需求,其中需要用echarts绘画一个地图,但是需要的地图是区/县…...

Java_简单模拟实现ArrayList_学习ArrayList
文章目录 一、 了解线性表和顺序表区别1.线性表2.顺序表 二、模拟实现1.定义接口2.定义MyArrayList3.成员变量以及构造方法4.实现打印数组5.实现add方法6.实现查找某个数是否存在contains或者某个数的下标indexOf7.获取或更改pos位置的值 get和set8.获取数组大小 size9.删除某个…...

动手学深度学习(一)深度学习介绍2
目录 二、起源 三、深度学习的成功案例: 四、特点: 五、小结: 二、起源 为了解决各种各样的机器学习问题,深度学习提供了强大的工具。 虽然许多深度学习方法都是最近才有重大突破,但使用数据和神经网络编程的核心思…...

vmware网络配置,VMware的三种网络模式详解与配置
vmware为我们提供了三种网络工作模式 vmware为我们提供了三种网络工作模式, 它们分别是: Bridged(桥接模式)、NAT(网络地址转换模式)、Host-Only(仅主机模式)。 VMware虚拟机的三种网络类型的适用场景如下…...

【Ubuntu】安装hbase
前提 需要安装java 安装 HBase 下载并解压 HBase 安装包: wget https://dlcdn.apache.org/hbase/2.5.7/hbase-2.5.7-bin.tar.gz tar -zxvf hbase-2.5.7-bin.tar.gz配置 HBase 环境变量: export HBASE_HOME/path/to/hbase-2.5.7 export PATH$PATH:$H…...

ubuntu16.04环境轻松安装和应用opencv4.9.0(基于源码编译)
目录 一、环境准备 1、安装cmake 2、安装依赖 3、从github上下载opencv4.9.0.zip 二、安装opencv4.9.0 1、解压4.9.0.zip 2、进入build目录编译 3、安装编译好的相关库 4、修改opencv配置文件并使其生效 5、添加PKG_CONFIG路径,并使其生效 三、opencv环境…...

FastBee开源物联网平台2.0开源版发布啦!!!
一、项目介绍 物美智能(wumei-smart)更名为蜂信物联(FastBee)。 FastBee开源物联网平台,简单易用,更适合中小企业和个人学习使用。适用于智能家居、智慧办公、智慧社区、农业监测、水利监测、工业控制等。 系统后端采用Spring boot;前端采用…...

【NeRF和NLP】一些观察感悟,碎碎念
NeRF的paper,有几个感想: NeRF读的时候感觉和diffusion思路特别像,训练目标是一个很小很小的子步骤,大大简化了训练难度NeRF建模的是“真实”世界,其用模型隐含的存储了真实世界的体素(场)模型…...

Python程序设计 基础数据类型
1.1 编程规范 注释 python注释也有自己的规范,在文章中会介绍到。注释可以起到一个备注的作用,团队合作的时候,个人编写的代码经常会被多人调用,为了让别人能更容易理解代码的通途,使用注释是非常有效的。 在说规范…...

浅谈安科瑞智能照明系统在马来西亚国家石油公司项目的应用
摘要:随着社会经济的发展及网络技术、通信技术的提高,人们对照明设计提出了新的要求,它不仅要控制照明光源的发光时间、 亮度,而且与其它系统来配合不同的应用场合做出相应的灯光场景。本文介绍了马亚西亚石油公司智能照明项目的应…...
Java面对对象
Java面向对象 面对对象概述,类与对象,继承,重写与重载,多态,抽象,封装,包,泛型,异常 面对对象概述 什么是面向对象(OOP) 面向对象(Object Ori…...

代码随想录算法训练营|day24
第七章 回溯算法 77.组合代码随想录文章详解总结 77.组合 以n5,k3为例 (1)for循环遍历,递归选择符合要求的值加入path,len(path)k时,返回 statrtIndex保证每次递归取到的值不重复 剪枝:i<n-(k-len(path))1 后续需要k-len(pat…...

嵌入式学习日记 16
共用体 union 共用体名 { 成员列表; //各个变量 }; //表示定义一个共用体类型 注意: 1.共用体 初始化 --- 只能给一个值,默认是给到第一个成员变量的 2.共用体成员变量辅助 共用体用的数据最终存储的 --- 应该是最后一次给到的值。 但是只能…...

【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记(完结)
从高层设计的角度去探讨框架需要关注的问题。 参考:速读《Vue.js 设计与实现》 - 掘金 (juejin.cn) 系列目录: 标题博客第一篇:框架设计概览【Vue.js设计与实现】第一篇:框架设计概览-阅读笔记第二篇:响应系统【Vue.…...

数据结构—动态查找表
动态查找介绍 1. 动态查找的引入:当查找表以线性表的形式组织时,若对查找表进行插入、删除或排序操作,就必须移动大量的记录,当记录数很多时,这种移动的代价很大。 2. 动态查找表的设计思想:表结构本身是…...

Hbase-2.4.11_hadoop-3.1.3集群_大数据集群_SSH修改默认端口22为其他端口---记录025_大数据工作笔记0185
其实修改起来非常简单,但是在大数据集群中,使用到了很多的脚步,也需要修改, 这里把,大数据集群,整体如何修改SSH端口,为22022,进行总结一下: 0.hbase-2.4.11的话,hbase集群修改默认SSH端口22,修改成22022,需要修改 需要修改/opt/module/hbase-2.4.11/conf/hbase-env.sh 这里…...