【项目日记(五)】第二层: 中心缓存的具体实现(上)

💓博主CSDN主页:杭电码农-NEO💓

⏩专栏分类:项目日记-高并发内存池⏪

🚚代码仓库:NEO的学习日记🚚

🌹关注我🫵带你做项目
  🔝🔝
开发环境: Visual Studio 2022


在这里插入图片描述

项目日记

  • 1. 前言
  • 2. 中心缓存的哈希桶结构
  • 3. span结构的具体实现
  • 4. 中心缓存类的定义
  • 5. 中心缓存如何分配小块儿内存?
  • 6. 中心缓存无内存时应该如何做?
  • 7. 总结

1. 前言

中心缓存起到一个承上启下的作用,
它负责给线程缓存分配小块儿的内
存,并且负责从页缓存申请大块儿内存

本章重点:

本篇文章着重讲解中心缓存的结构
包括span类的具体成员.并且会讲解
中心缓存是如何给线程缓存分配内存
并且是如何向页缓存申请/内存的!


2. 中心缓存的哈希桶结构

在对整个项目的结构做介绍的文章
中以及提到过中心缓存的结构了,
值得注意的是中心缓存使用的是桶锁
即每个哈希桶也就是每个spanlist都
又一个互斥锁

在这里插入图片描述


3. span结构的具体实现

span的具体结构:

shared.h文件:

//管理多个连续页的大块内存跨度结构,centralcache的哈希桶中链接的就是这种结构
class SpanData
{
public:
	PAGE_ID _pageid = 0;//32位下,程序地址空间,2^32byte,一页8kb=2^13byte,一共有2^19页
	size_t _n = 0;//页数
	SpanData* _next = nullptr;
	SpanData* _prev = nullptr;
	size_t _useCount = 0;//span中切分好的小对象有几个被使用了
	void* _freeList = nullptr;//切分好的小块内存的自由链表
	bool _isUse = false; //这个span是否正在被使用,若没有被使用则可能被pagecache合并成为大页
	size_t _objSize = 0; //切分好的小对象的大小,方便后面删除的时候可以直接知道它的大小
};
class SpanList//双向带头循环链表
{
public:
	SpanList()
	{
		_head = new SpanData;
		_head->_next = _head;
		_head->_prev = _head;
	}
	SpanData* Begin()
	{
		return _head->_next;
	}
	SpanData* End()
	{
		return _head;
	}
	bool Empty()//判断这个桶中是不是没有span
	{
		return _head->_next == _head;
	}
	void Insert(SpanData* pos, SpanData* newSpan)
	{
		assert(pos && newSpan);
		SpanData* prev = pos->_prev;
		prev->_next = newSpan;
		newSpan->_prev = prev;
		newSpan->_next = pos;
		pos->_prev = newSpan;
	}
	void Erase(SpanData* pos)
	{
		assert(pos);
		assert(pos != _head);
		/*if (pos == _head)
		{
			int x = 0;
		}*/
		SpanData* prev = pos->_prev;
		SpanData* next = pos->_next;
		prev->_next = next;
		next->_prev = prev;
	}
	void PushFront(SpanData* span)
	{
		Insert(Begin(), span);
	}
	SpanData* PopFront()
	{
		SpanData* front = _head->_next;
		Erase(front);
		return front;//erase中没有将此节点释放
	}
	SpanData* _head = nullptr;
	std::mutex _mtx;//桶锁
}; 

对成员变量use_count的解释:
use_count为0时,代表这个span
中所有被分配出去的小块儿内存
都被线程缓存还回来了,此时可直接
将这个span从中心缓存还给页缓存


4. 中心缓存类的定义

并且,中心缓存整体被设计为了单例模式:

CentralCache.h文件:

lass CentralCache
{
public:
	static CentralCache* GetInstance()
	{
		return &_singleton;
	}
	// 从中心缓存获取一定数量的对象(小块儿内存)给thread cache
	size_t FetchRangeObj(void*& start, void*& end, size_t massNum, size_t size);//拿n个内存对象,大小是byte_size,start和end是输出型参数

	// 从SpanList获取一个非空的span
	SpanData* GetOneSpan(SpanList& list, size_t size);

	// 将ThreadCache返回来的内存重新挂在CentralCache的span
	void ReleaseListToSpans(void* start, size_t byte_size);
private:
	CentralCache(){}
	CentralCache(const CentralCache&) = delete;
private:
	SpanList _spanlist[N_FREE_LIST];//中心缓存的桶映射规则和Thread一样,208个桶
	static CentralCache _singleton;//单例模式
};
CentralCache CentralCache::_singleton = new CentreaCache();

5. 中心缓存如何分配小块儿内存?

FetchRangeObj函数我们并不陌生,
在线程缓存中,当桶中没有小块儿内存
时就是调用此函数来中心缓存获取的!

分配内存的基本步骤1:

中心缓存会先找到对应的哈希桶,然后
去桶中取一个非空的span结构,再将这
个span结构中切分好的小块儿内存分
配给线程缓存使用

CentralCache.h文件:

size_t CentralCache::FetchRangeObj(void*& start, void*& end, size_t massNum, size_t size)
{
	size_t index = AlignmentRule::Index(size);//找到对应的哈希桶
	_spanlist[index]._mtx.lock();//加锁
	SpanData* span = GetOneSpan(_spanlist[index], size);//从桶中获取一个span结构
	assert(span && span->_freeList);
	//从span中获取massnum个对象,若没有这么多对象的话,有多少就给多少
	start = span->_freeList;//把start指向首地址
	end = start;
	int factcount = 1;//实际分配给线程缓存的对象个数
	int i = 0;
	while (*(void**)end != nullptr && i< massNum - 1)
	{
		end = *(void**)end;
		i++;
		factcount++;
	}
	span->_useCount += factcount;
	span->_freeList = *(void**)end;
	*(void**)end = nullptr;
	_spanlist[index]._mtx.unlock();//解锁
	return factcount;
}

6. 中心缓存无内存时应该如何做?

分配内存的基本步骤2:

若对应的哈希桶中的span为空,也
就是中心缓存无内存了,就会调用
NewSpan去页缓存获取一个新的
span结构,然后把新的span切分为
小块儿内存后再给线程缓存使用!

SpanData* CentralCache::GetOneSpan(SpanList& list, size_t size)
{
	SpanData* it = list.Begin();
	//遍历centralcache的中固定桶的所有span,若找到有不为空的freelist,则直接返回
	while (it != list.End())
	{
		if (it->_freeList != nullptr)//如果中心缓存有非空span,直接返回
			return it;
		else
			it = it->_next;
	}
	//先把centralcache的桶锁解除,这样如果其他线程释放内存对象回来不会阻塞
	list._mtx.unlock();
	//走到这儿证明这个桶中没有span小对象了,去找pagecache要span
	//直接在这里将页缓存结构加锁,Newspan内就不用加锁了
	PageCache::GetInstance()->_mtx.lock();
	SpanData* span = PageCache::GetInstance()->NewSpan(AlignmentRule::NumMovePage(size));//传的参数是要申请的页数,size越大对应的页就应该越大
	span->_isUse = true;//将这个span的状态修改为正在使用
	span->_objSize = size;
	PageCache::GetInstance()->_mtx.unlock();
	//下面的内容不需要加锁,因为获取到的span只有我这个线程有,其他线程访问不到
	char* address = (char*)((span->_pageid) << PAGE_SHIFT); //这个页的起始地址是页号*8*1024,第0页的地址是0,以此类推
	size_t bytes = span->_n << PAGE_SHIFT; //计算这个span总共有多少个字节,用_n(页数)*8*1024
	//接下来要将这个span的大块内存切分成小块内存用自由链表连接起来
	char* end = address + bytes;//address和end对应空间的开头和结尾
	//1. 先切一块下来去做头,方便后续尾插
	span->_freeList = address;
	address += size;
	void* cur = span->_freeList;
	while (address < end)//2. 遍历空间尾插
	{
		*(void**)cur = address;
		cur = *(void**)cur;
		address += size;
	}
	*(void**)cur = nullptr;
	//插入时需要加锁,否则指向可能乱掉
	list._mtx.lock();
	list.PushFront(span);
	return span;
}

值得注意的是,获取到span后我们要通过这个span的页数来知道这个span有多少内存,并且要通过这个span在程序地址空间的页号来判断这份内存的起始地址是多少!第0页的地址是0000 0000,第一页的地址是8KB,以此类推

在这里插入图片描述


7. 总结

中心缓存这里给线程缓存分配内存时是
有两种情况的,当中心缓存无内存时就
会向页缓存索要,而本篇文章只讲解了
申请内存的过程,而当线程缓存将内存
还回来后,还有可能将span还给页缓存


🔎 下期预告:中心缓存的具体实现(下)🔍

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

K8S搭建(centos)十、Dashboard配置(主节点)

天行健&#xff0c;君子以自强不息&#xff1b;地势坤&#xff0c;君子以厚德载物。 每个人都有惰性&#xff0c;但不断学习是好好生活的根本&#xff0c;共勉&#xff01; 文章均为学习整理笔记&#xff0c;分享记录为主&#xff0c;如有错误请指正&#xff0c;共同学习进步。…

为什么 FPGA 比 CPU 和 GPU 快?

FPGA、GPU 与 CPU——AI 应用的硬件选择 现场可编程门阵列 (FPGA) 为人工智能 (AI) 应用带来许多优势。图形处理单元 (GPU) 和传统中央处理单元 (CPU) 相比如何&#xff1f; 人工智能&#xff08;AI&#xff09;一词是指能够以类似于人类的方式做出决策的非人类机器智能。这包…

秋招实习,刷题网站推荐

codefun2000.com 优点1: 题目全部改编自公司笔试真题&#xff0c;可以做做往年真题练手。 优点2: 该平台和公司笔试模式一样&#xff0c;都是Acm输入输出&#xff0c;更有利于准备秋招。 优点3: 平台主页有博客&#xff0c;以及各大公司真题知识点思维导图&#xff0c;讲…

第十五回 杨志押送金银担 吴用智取生辰纲-Ubuntu下wifi 无线网络的配置

晁盖等七人排了座次&#xff0c;大家商定在黄泥冈附近动手&#xff0c;正好邀请白胜参与&#xff0c;这样七人对应七星聚义&#xff0c;白胜对应北斗上的白光。 却说大名府梁中书收买了十万贯庆贺生日礼物&#xff0c;准备派杨志去押送。梁中书本意派军士押送&#xff0c;杨志说…

【重点】【DP】123.买卖股票的最佳时机III

题目 法1&#xff1a;单次遍历&#xff0c;Best! class Solution {public int maxProfit(int[] prices) {int f1 -prices[0], f2 0, f3 -prices[0], f4 0;for (int i 1; i < prices.length; i) {f1 Math.max(f1, -prices[i]);f2 Math.max(f2, f1 prices[i]);f3 Ma…

机器学习工程师在人工智能时代的角色

机器学习工程师在人工智能时代的角色 在当今的数字时代&#xff0c;人工智能&#xff08;AI&#xff09;已成为许多行业不可或缺的一部分。从流程自动化到增强客户体验&#xff0c;人工智能具有改变企业的巨大潜力。这一变革性技术的核心是机器学习&#xff0c;该领域专注于开…

从云计算到物联网:虚拟化技术的演变与嵌入式系统的融合

文章目录 一、硬件性能提升&#xff1a;摩尔定律与嵌入式虚拟化二、CPU多核技术&#xff1a;为嵌入式虚拟化提供支持三、业务负载整合&#xff1a;嵌入式虚拟化的核心需求四、降低硬件成本&#xff1a;虚拟化技术的经济效益五、软件重用与移植&#xff1a;虚拟化技术的优势六、…

近期孩子燃放烟花已引发多起火灾 富维图像烟火识别防止悲剧发生

近期&#xff0c;孩子们燃放烟花造成的火灾事件频发&#xff0c;这不仅威胁到社区的安全&#xff0c;更触动了每个家庭的心。在这样的背景下&#xff0c;“预防胜于治疗”显得尤为重要。北京富维图像公司的FIS智能图像识别系统&#xff0c;就是这样一款能够及时防止悲剧发生的创…

旷视low-level系列(一):Bayer Pattern Unification and Bayer Preserving Au

文章目录 1. Motivation2. Contribution3. Methods3.1 BayerUnify3.2 BayerAug 4. CommentsReference 1. Motivation 对于RAW域去噪&#xff0c;通常会将单通道bayer格式的RAW图打包成4通道&#xff0c;然后送入神经网络。不同厂家生产的sensor出的RAW图可能具有不同的bayer模…

QtRVSim(二)一个 RISC-V 程序的解码流程

继上一篇文章简单代码分析后&#xff0c;本文主要调研如何实现对指令的解析运行。 调试配置 使用 gdb 工具跟踪调试运行。 c_cpp_properties.json 项目配置&#xff1a; {"name": "QtRvSim","includePath": ["${workspaceFolder}/**&quo…

21.云原生之GitLab pipline语法实战(CI基础)

云原生专栏大纲 文章目录 gitlab-ci.yml 介绍GitLab中语法检测gitlab-ci.yml 语法job定义作业before_script和after_scriptstages定义阶段tages指定runnerallow_failure运行失败when控制作业运行retry重试timeout超时parallel并行作业only & exceptrulescache 缓存cache:p…

ETL能实现什么流程控制方式?

随着大数据时代的到来&#xff0c;数据处理工具成为各个行业中不可或缺的一部分。运用数据处理工具&#xff0c;能够大幅度帮助开发人员进行数据处理等工作&#xff0c;以及能够更好的为企业创造出有价值的数据。那在使用ETL工具时&#xff0c;我们往往会通过ETL平台所携带的组…

萝卜大杂烩 | 一篇文章扫盲Python、NumPy 和 Pandas,建议收藏!(适合初学者、python入门)

本文来源公众号“萝卜大杂烩”&#xff0c;仅用于学术分享&#xff0c;侵权删&#xff0c;干货满满。 原文链接&#xff1a;长文预警&#xff0c;一篇文章扫盲Python、NumPy 和 Pandas&#xff0c;建议收藏慢慢看 Python作为简单易学的编程语言&#xff0c;想要入门还是比较容…

2、鼠标事件、键盘事件、浏览器事件、监听事件、冒泡事件、默认事件、属性操作

一、鼠标事件 1、单击事件&#xff1a;onclick <body><header id"head">我是头部标签</header> </body> <script> var head document.getElementById("head")head.onclick function () {console.log("我是鼠标单击…

单片机设计_智能蓝牙电子秤(51单片机、HX711AD)

想要更多项目私wo!!! 一、电路设计 智能蓝牙电子称由51单片机、HX711AD称重模块、HC-05蓝牙模块、LCD1602等电路组成硬件部分,然后上传至APP。 二、运行结果 三、部分代码 #include "main.h" #include "HX711.h" #include "uart.h" #include …

podman+centos和docker+alpine中作性能对比遇到的问题及解决

1.dockeralpine中遇到这个问题 这是由于缺少相关的配置和依赖造成的 通过以下命令在alpine中安装相关配置 apk add --no-cache build-base cairo-dev cairo cairo-tools jpeg-dev zlib-dev freetype-dev lcms2-dev openjpeg-dev tiff-dev tk-dev tcl-dev 2.alpine中python找…

API网关-Apisix RPM包方式自动化安装配置教程

文章目录 前言一、简介1. etcd简介2. APISIX简介3. apisix-dashboard简介 二、Apisix安装教程1. 复制脚本2. 增加执行权限3. 执行脚本4. 浏览器访问5. 卸载Apisix 三、命令1. Apisix命令1.1 启动apisix服务1.2 停止apisix服务1.3 优雅地停止apisix服务1.4 重启apisix服务1.5 重…

【云原生】认识docker容器操作命令

目录 一、容器操作命令 1、创建容器 2、删除容器以及停止容器运行 3、查看容器的运行状态 4、查看容器的详细信息 5、将容器的文件传输到宿主机以及将宿主机的文件传输到容器中 6、批量删除容器 7、进入容器 二、容器的迁移 1、先在容器中创建测试文件 2、将容器存储…

洛谷 P5635 【CSGRound1】天下第一

原址链接 P5635 【CSGRound1】天下第一 先看标签 搜索&#xff1f;模拟&#xff1f;用不着这么复杂 创建函数a(int x,int y,int p) a(int x,int y,int p){if(x<0){return 1;}x (xy)%p;if(y<0){return 2;}y (xy)%p;return a(x,y,p); }写入主函数 #include<iostrea…

防御保护----防火墙的安全策略、NAT策略实验

实验拓扑&#xff1a; 实验要求&#xff1a; 1.生产区在工作时间&#xff08;9&#xff1a;00-18&#xff1a;00&#xff09;内可以访问DMZ区&#xff0c;仅可以访问http服务器&#xff1b; 2.办公区全天可以访问DMZ区&#xff0c;其中10.0.2.10可以访问FTP服务器和HTTP服务器…