【项目日记(7)】第三层:页缓存的具体实现(上)

目录

  • 前言
  • 1. 页缓存的具体结构
  • 2. 页缓存分配内存的全过程
  • 3. 页缓存分配内存的代码实现
  • 4. 优化代码,并完全脱离malloc
  • 5. 总结以及代码拓展

前言

在页缓存这一层中,负责给中心缓存分配大块儿的内存,以及合并前后空闲的内存,这一层为整体加锁!

本章重点:

本篇文章着重讲解内存池第三层:页缓存的基本成员变量和函数,以页缓存的具体结构是怎样的。了解完基础结构后,会详解讲解中心缓存层来申请内存时的具体步骤!

1. 页缓存的具体结构

页缓存也是一个哈希桶结构,但它的映射规则和前两层不同,它的规则是:

K号桶中的大块儿内存就是K页

在这里插入图片描述

在pagecache.h文件中

// 1.central cache向page cache取,关注的是需要几页span
// page cache也是哈希桶,用的是直接定址
// 2.也使用单例饿汉模式
class PageCache
{
public:
	static PageCache* GetInstance()
	{
		return &_sInst;
	}

	// 获取从对象到span的映射
	Span* MapObjectToSpan(void* obj);

	// 释放空闲span回到Pagecache,并合并相邻的span
	void ReleaseSpanToPageCache(Span* span);

	// 获取一个k页的span
	Span* NewSpan(size_t k);

	std::mutex _pageMtx;
private:
	SpanList _spanList[NPAGES];
	ObjectPool<Span> _spanPool; // 把使用new/delete的地方换成定长内存池的设计
	
	// 有内存块的地址就可以计算出页号,再加上这个页号和span之间的映射
	// 就可以找到内存块在哪个span
	//std::unordered_map<PAGE_ID, Span*> _idSpanMap; 

	TCMalloc_PageMap1<32 - PAGE_SHIFT>_idSpanMap;
private:
	PageCache()
	{}
	PageCache(const PageCache&) = delete;

	static PageCache _sInst;
};

2. 页缓存分配内存的全过程

当中心缓存中没有内存时,会去页缓存申请一个span结构,要经过下面几步:

1.根据中心缓存的桶号来确定申请的span是几页的;

2.根据中心缓存想要申请的页数,找到页缓存中对应的桶(k页对应k号桶);

3.情况一: 页缓存的K号桶中存在span结构,直接将这块儿内存返回给中心缓存;

4.情况二: 页缓存的K号桶没有span结构,但是K+1到128号桶中存在span结构,假设n号桶有span,则将这个大页的span切分为一个k页的span和一个n-k页的span,k页的span返回给中心缓存去使用,而将n-k页的span重新挂在n-k号桶中;

5.情况三: k到128号桶都没有span,此时页缓存会向系统申请一份128页大小的内存,并挂在128号桶中,再将这个128页的span切分为k页的span和128-k页的span,也就转换为了情况二!

在这里插入图片描述

并且在这个过程中,页缓存将一个span分配给中心缓存后,会记录下来这块儿内存的页号和span的映射关系,方便后续回收内存的时候再使用!

3. 页缓存分配内存的代码实现

// 获取一个k页的span
Span* PageCache::NewSpan(size_t k)
{
	// 假设central cache需要2page:
	// 刚开始哈希桶为空,没有一个span,向堆申请一个128page 的span
	// 把128page 切分成2page的span和126page的span
	// 2page返回给central cache使用,126page挂到126号桶里

	assert(k > 0);

	// 如果大于128页,直接找堆要
	if (k > NPAGES - 1)
	{
		void* ptr = SystemAlloc(k);
		Span* span = new Span;
		
		span->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT;
		span->_n = k;

		_idSpanMap[span->_pageId] = span;
		
		return span;
	}

	// 先检查第k个桶里有没有span
	// 如果有,直接弹出去给上一层的central cache用于切成一个个小内存对象
	if (!_spanList[k].Empty())
	{
		Span* kSpan =  _spanList[k].PopFront();

		// 建立pageid和span的映射,方便central cache回收小块内存时,查找对应的span
		for (PAGE_ID i = 0; i < kSpan->_n; i++)
		{
			//_idSpanMap[kSpan->_pageId + i] = kSpan;
			_idSpanMap.set(kSpan->_pageId + i, kSpan);
		}

		return kSpan;
	}

	// 检查一下后面的桶里面有没有span,如果有,就可以把它进行切分
	// 进而挂到page cache的不同桶里
	for (size_t i = k + 1; i < NPAGES; i++)
	{
		if (!_spanList[i].Empty())
		{
			// 只要其中一个桶里有span,直接取下来进行切分
			// 切分成一个k页的span和一个n-k页的span
			// k页的span返回给central cache,n-k页的span挂到第n-k个桶
			Span* nSpan = _spanList[i].PopFront();
			//Span* kSpan = new Span;
			Span* kSpan = _spanPool.New();

			// 在nSpan的头部切一个k页下来
			// k页的span返回
			kSpan->_pageId = nSpan->_pageId;
			kSpan->_n = k;

			// 原来桶里的span被切掉了k页,所以页号要往前加k,页数要减k
			nSpan->_pageId += k;
			nSpan->_n -= k;

			// 再把剩下的页挂到对应的桶里
			_spanList[nSpan->_n].PushFront(nSpan);

			// 存储nSpan的首尾页号跟nSpan映射,方便page cache回收内存时,进行合并查找
			_idSpanMap[nSpan->_pageId] = nSpan;
			_idSpanMap[nSpan->_pageId + nSpan->_n - 1] = nSpan;

			// 建立pageid和span的映射,方便central cache回收小块内存时,查找对应的span
			for (PAGE_ID i = 0; i < kSpan->_n; i++)
			{
				_idSpanMap[kSpan->_pageId + i] = kSpan;
			}

			return kSpan;
		}
	}

	// 走到这个位置就说明后面没有大页的span了
	// 这时就去找堆要一个128页的span
	//Span* bigSpan = new Span;
	Span* bigSpan = _spanPool.New();

	void* ptr = SystemAlloc(NPAGES - 1);
	bigSpan->_pageId = (PAGE_ID)ptr >> PAGE_SHIFT; // 地址->页号的转换
	bigSpan->_n = NPAGES - 1;

	// 再把从系统上获取到的128页挂到第128个桶里
	_spanList[bigSpan->_n].PushFront(bigSpan);

	return NewSpan(k);
}

这个地方有一个设计的比较巧妙的点,那就是出现情况三的时候,向系统申请了128页的空间后,再次调用这个函数就一定会出现情况二,从而在for循环中走完整个过程。

4. 优化代码,并完全脱离malloc

细心的同学会发现,在这个函数中使用到了new操作符,然而了解new底层原理的同学应该知道,new的底层实际上是用的malloc来申请的空间,但是我们这个项目就是为了完全脱离malloc函数来实现一个多线程下高效的内存池,所以这里一定不能使用new!

使用之前编写的定长池来舍弃new!

首先, 在页缓存类中添加上一个成员变量: 定长池类, 然后在使用new的地方,把new全部替换为用定长池申请空间!

在这里插入图片描述

5. 总结以及代码拓展

页缓存分配内存的一环设计的是非常的巧妙,但是页缓存真正巧妙的地方是在合并空闲内存的一环!

对代码的拓展:
我们会发现页缓存结构中调用了好几次向系统申请内存的函数,这个地方只做了解,会用接口就行。

inline static void* SystemAlloc(size_t kpage)//申请kpage页内存
{
#ifdef _WIN32
	void* ptr = VirtualAlloc(0, kpage << PAGE_SHIFT, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
#else
	// linux下brk mmap等直接向系统申请内存的方式
#endif

	if (ptr == nullptr)
		throw std::bad_alloc();
	return ptr;
}

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

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

相关文章

Python + 深度学习从 0 到 1(03 / 99)

希望对你有帮助呀&#xff01;&#xff01;&#x1f49c;&#x1f49c; 如有更好理解的思路&#xff0c;欢迎大家留言补充 ~ 一起加油叭 &#x1f4a6; 欢迎关注、订阅专栏 【深度学习从 0 到 1】谢谢你的支持&#xff01; ⭐ 神经网络的数据表示 – 张量 你可能对矩阵很熟悉&a…

使用Docker-compose部署SpringCloud项目

docker编写dockfile遇到的问题&#xff1a; 需要在docker-compose.yml文件下执行命令 docker-compose.yml文件格式的问题 1和2处空2格&#xff0c;3处空1格&#xff0c;4为本地配置文件目录&#xff0c;5为docker容器的目录&#xff0c;version为自己安装的docker-compose版本 …

【机器学习】【朴素贝叶斯分类器】从理论到实践:朴素贝叶斯分类器在垃圾短信过滤中的应用

&#x1f31f; 关于我 &#x1f31f; 大家好呀&#xff01;&#x1f44b; 我是一名大三在读学生&#xff0c;目前对人工智能领域充满了浓厚的兴趣&#xff0c;尤其是机器学习、深度学习和自然语言处理这些酷炫的技术&#xff01;&#x1f916;&#x1f4bb; 平时我喜欢动手做实…

Tonghttpserver6.0.1.3 使用整理(by lqw)

文章目录 1.声明2.关于单机版控制台和集中管理控制台3.单机版控制台3.1安装&#xff0c;启动和查看授权信息3.2一些常见的使用问题&#xff08;单机控制台&#xff09;3.3之前使用的是nginx&#xff0c;现在要配nginx.conf上的配置&#xff0c;在THS上如何配置3.4如何配置密码过…

BUUCTF Pwn ciscn_2019_es_2 WP

1.下载 checksec 用IDA32打开 定位main函数 发现了个假的后门函数&#xff1a; 看看vul函数&#xff1a; 使用read读取 想到栈溢出 但是只有48个 只能覆盖EBP和返回地址 长度不够构造 所以使用栈迁移&#xff1a; 栈迁移需要用到leave ret 使用ROPgadget找地址&#xff1a; …

6.若依数据字典

数据字典 维护系统中常见的静态数据&#xff0c;例如&#xff1a;性别、状态等。 好处 不写死在页面上&#xff0c;而是通过数据库来维护&#xff0c;因为如果要修改&#xff0c;则只需要改数据库中的数据即可&#xff0c;不用每个地方都修改了。 字典类型的管理 字典数据的…

JVM学习-内存结构(二)

一、堆 1.定义 2.堆内存溢出问题 1.演示 -Xmx设置堆大小 3.堆内存的诊断 3.1介绍 1&#xff0c;2都是命令行工具&#xff08;可直接在ideal运行时&#xff0c;在底下打开终端&#xff0c;输入命令&#xff09; 1可以拿到Java进程的进程ID&#xff0c;2 jmap只能查询某一个时…

rust windwos 两个edit框

use winapi::shared::minwindef::LOWORD; use windows::{core::*,Win32::{Foundation::*,Graphics::Gdi::{BeginPaint, EndPaint, PAINTSTRUCT},System::LibraryLoader::GetModuleHandleA,UI::WindowsAndMessaging::*,}, };// 两个全局静态变量&#xff0c;用于保存 Edit 控件的…

PostgreSQL 数据库连接

title: PostgreSQL 数据库连接 date: 2024/12/29 updated: 2024/12/29 author: cmdragon excerpt: PostgreSQL是一款功能强大的开源关系数据库管理系统,在现代应用中广泛应用于数据存储和管理。连接到数据库是与PostgreSQL进行交互的第一步,这一过程涉及到多个方面,包括连…

【服务器项目部署】⭐️将本地项目部署到服务器!

目录 &#x1f378;前言 &#x1f37b;一、服务器选择 &#x1f379; 二、服务器环境部署 2.1 java 环境部署 2.2 mysql 环境部署 &#x1f378;三、项目部署 3.1 静态页面调整 3.2 服务器端口开放 3.3 项目部署 ​ &#x1f379;四、测试 &#x1f378;前言 小伙伴们大家好…

网络层知识点梳理

网络层的作用 实现点到点服务的数据透明传送&#xff0c;具体功能包括寻址和路由选择、连接的建立、保持和终止点等。它提供的服务使传输层不需要了解网络中的数据传输和交换技术 网络层单位是分组网际层协议IP ARP地址解析协议 根据IP地址获取物理地址 RARP反地址解析协议 根据…

Spring Boot教程之四十:使用 Jasypt 加密 Spring Boot 项目中的密码

如何使用 Jasypt 加密 Spring Boot 项目中的密码 在本文中&#xff0c;我们将学习如何加密 Spring Boot 应用程序配置文件&#xff08;如 application.properties 或 application.yml&#xff09;中的数据。在这些文件中&#xff0c;我们可以加密用户名、密码等。 您经常会遇到…

windows 上安装nginx , 启停脚本

windows 上安装nginx , 启停脚本 cmd win 查看进程 tasklist /fi "imagename eq nginx.exe" 杀死进程 taskkill -pid 16212 -f 访问 http://127.0.0.1:8081/# 用脚本管理&#xff0c; 创建文件 kill.txt echo off chcp 65001 setlocal enabledel…

【Rust自学】7.5. use关键字 Pt.2 :重导入与换国内镜像源教程

喜欢的话别忘了点赞、收藏加关注哦&#xff0c;对接下来的教程有兴趣的可以关注专栏。谢谢喵&#xff01;(&#xff65;ω&#xff65;) 7.5.1. 使用pub use重新导入名称 使用use将路径导入作用域内后。该名称在词作用域内是私有的。 以上一篇文章的代码为例&#xff1a; m…

vulnhub jangow靶机

1.扫描靶机IP arp-scan -l如果扫不到靶机的话根据以下配置 启动时点击第二个 按回车 继续选择第二个 按e进入编辑 删除"recovery nomodeset" 在末尾添加"quiet splash rw init/bin/bash" Ctrlx 启动进入如下界面 passwd修改root密码 重启电脑登录root修…

Redis Java 集成到 Spring Boot

Hi~&#xff01;这里是奋斗的明志&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f331;&#x1f331;个人主页&#xff1a;奋斗的明志 &#x1f331;&#x1f331;所属专栏&#xff1a;Redis &#x1f4da;本系列文章为个人学习笔…

FPGA实现HDMI输出

FPGA实现HDMI输出 对应的视频讲解 FPGA实现HDMI输出 FPGA实现HDMI输出有两种方式 采用专门的HDMI芯片使用RTL完成TMDS编码 受限于FPGA本身时钟频率的限制&#xff0c;使用RTL完成TMDS编码的方式是很难完成高帧率的HDMI输出的&#xff0c;比如1080P60Hz的像素时钟为148.5MHz&…

uniapp——微信小程序,从客户端会话选择文件

微信小程序选择文件 文章目录 微信小程序选择文件效果图选择文件返回数据格式 API文档&#xff1a; chooseMessageFile 微信小程序读取文件&#xff0c;请查看 效果图 选择文件 /*** description 从客户端会话选择文件* returns {String} 文件路径*/ const chooseFile () &g…

SpringCloudAlibaba实战入门之路由网关Gateway初体验(十一)

Spring Cloud 原先整合 Zuul 作为网关组件,Zuul 由 Netflix 公司提供的,现在已经不维护了。后面 Netflix 公司又出来了一个 Zuul2.0 网关,但由于一直没有发布稳定版本,所以 Spring Cloud 等不及了就自己推出一个网关,已经不打算整合 zuul2.0 了。 一、什么是网关 1、顾明…

C#WPF基础介绍/第一个WPF程序

什么是WPF WPF&#xff08;Windows Presentation Foundation&#xff09;是微软公司推出的一种用于创建窗口应用程序的界面框架。它是.NET Framework的一部分&#xff0c;提供了一套先进的用户界面设计工具和功能&#xff0c;可以实现丰富的图形、动画和多媒体效果。 WPF 使用…