@bit::Shadow
✧(≖ ◡ ≖✿ 

目录

三级缓冲区图解

各级缓冲区成员构成表

整体图解

三级缓冲区结构图

二级、三级缓冲区架构图

三缓存整体缩略图

ThreadCahe图解

​编辑

CentralCache图解

PageCache图解

PageCache

GetOneSpan()

GetSpans()

mutex分析


针对我个人的复习提醒:本章节首先分析了整体架构,复习时可首先回顾。

三级缓冲区图解

把握框架设计,熟悉整体架构。

各级缓冲区成员构成表

整体图解

三级缓冲区结构图

二级、三级缓冲区架构图

三缓存整体缩略图

ThreadCahe图解

CentralCache图解

PageCache图解

PageCache

PageCache是全局唯一的三级内存池中最底层的“大块内存批发商”,以多页(N*8K)为最小管理单位,负责向操作系统申请内存、按需切给CentralCache。(后续会并回收♻️不再使用的Span。)

三级缓冲区结构

注意:是双向带头循环链表

GetOneSpan()

功能:返回Span* 作为链表裁剪的对象(TargetNum、RealNum)。

定位:作为PageCache中获取Span的首次入口:

1.若直接存在则返回(判断依据为非空)。
2.从PageCache区找。

Span* CentralCache::GetOneSpan(SpanList& spl, size_t AlignSize)
{
	//ThreadCache需要满足AlignSize的span
	//spl内找

	//spl._mtx.lock();桶锁依然锁着

	Span* bg = spl.Begin();
	Span* ed = spl.End();
	while (bg != ed)
	{
		if (bg->_freeList)
			return bg;
		bg = bg->_nextSpan;
	}
	//spl内没有找到,spl所有span下为空/spl内无span
	
	spl._mtx.unlock();
//为什么解锁后加锁让可能大于等于2的线程在三级缓冲区竞争锁?
//没有考虑到,若一级缓冲区恰好在间歇处释放内存回来到CentralCache的特定桶下造成
    ,直接返回的情况。

	//去PageCache三级缓冲区寻找
	PageCache::GetInstance()->_mtx.lock();

	size_t npage = SizeClass::NumMovePage(AlignSize);
        //获取span数,此处应为AlignSize不是课件内size
	Span* span = PageCache::GetInstance()->GetSpans(npage);

	PageCache::GetInstance()->_mtx.unlock();
	//需加锁:桶锁or全局锁? 考虑到进入三级缓冲区的频率极低,使用全局锁

//	//spl._mtx.lock();//线程独立,无需加锁
	
	//仅切分span为单链形式即可,无需动属性值
	//此处应为AlignSize不是课件内size
//char* start = (char*)span;
	char* start = (char*)(span->_pageId << PAGE_SHIFT);
	//忽略warning::(62,28): warning C4293: “<<”: Shift 计数为负或过大,其行为未定义
	size_t bytes = (span->_n) << PAGE_SHIFT;
	char* end = start + bytes;
	void* front = span->_freeList = start;
	//int i = 0;/*
	//printf("i:%p\n", &i);*/

	while (start < end)
	{
		//i++;
		*(void**)front = start;
		front = *(void**)front;
		start += AlignSize;
	}
	*(void**)front = nullptr;

	//插入到CentralCache特定桶下,需要加锁
	spl._mtx.lock();
	spl.HeadInsert(span);
	return span;
	//仅写完此模块(NewSpan未写)依看不出来spl._mtx的锁问题。
}

GetSpans()

功能:返回获取页数固定的Span* 。

直接访问PageCache:: _spanList[]的首次入口

1.直接定位查询。
2.向下查找,存在则切分。(new span)供切出去返回的实体。
3.系统级申请。

Span* PageCache::GetSpans(size_t pageNum)
{
	//三级缓冲区获取span
	//1.直接定位 2.向下找 3.VirtualAlloc()堆区申请
	Span* span = PageCache::GetInstance()->_spanList[pageNum].Begin();
	if (span->_nextSpan != span)
	{
		//PageCache拥有span非空链表
		//SpanList的头删
		PageCache::GetInstance()->_spanList[pageNum].ErasePos(span);
		return span;//这也是ErasePos内不能delete的原因。要向上供给,这块内存属于
	}
	//[pageNum]下没有span
						
	//2.在PageCache内向下找
	for(int i = pageNum+1;i < 129;i++)
	{
		Span* _span = PageCache::GetInstance()->_spanList[i].Begin();
		if (_span->_nextSpan != _span)
		{
			// 找到了需pageNum块,多出了i-pageNum块
			// 考虑到此部分代码在"3."情况下的复用可以额外封装,也可以使用巧妙的递归解决。
			// 头删
			PageCache::GetInstance()->_spanList[i].ErasePos(_span);
			//分割与插入
/*Span* insertSpan = (Span*)((PAGE_ID)_span + pageNum << PAGE_SHIFT);
insertSpan->_nextSpan = insertSpan;
insertSpan->_prevSpan = insertSpan;*/
//强制类型转换造成的内存越界访问
			Span* insertSpan = new Span;//堆申请!!!
			insertSpan->_n = i - pageNum;
			insertSpan->_pageId = _span->_pageId+pageNum;

			_span->_n = pageNum;
			_span->_pageId += pageNum;

			PageCache::GetInstance()->_spanList[i - pageNum].HeadInsert(insertSpan);//剩余块头插

			return _span;
		}
	}
	//3.[i]向下全空,向系统申请128页--128*8K
	void* ptr = SizeClass::SystemAlloc(128);//无需检查失败VirtualAlloc()
	//Span* test = new Span;
	Span* m128span = (Span*)ptr;//调用自动生成的拷贝赋值,无法自己指向自己
//
	m128span->_nextSpan = m128span;
	m128span->_prevSpan = m128span;
	m128span->_n = 1;
	m128span->_pageId = (PAGE_ID)m128span >> PAGE_SHIFT;
	
	_spanList[128].HeadInsert(m128span);

	return GetSpans(pageNum);
}

mutex分析

两个互斥锁:SpanList桶锁+PageCache的全局互斥锁。

视频口述

一级缓冲区衔接二三级缓冲区

二级、三级缓冲区锁管理


 回收逻辑过于简单(仅作以下图示)

无优化版本:申请回收全逻辑gitee🔗测试跑通

感谢支持,长期连载。
欢迎关注

Logo

智能硬件社区聚焦AI智能硬件技术生态,汇聚嵌入式AI、物联网硬件开发者,打造交流分享平台,同步全国赛事资讯、开展 OPC 核心人才招募,助力技术落地与开发者成长。

更多推荐