大家好,这里是物联网心球。
前面我们学习了很多Linux内存方面的知识,比如:虚拟地址空间,进程空间,内存映射,页表机制等,我们学了这么多知识,似乎对Linux内存似懂非懂,为什么会出现这样的问题?原因在于我们缺乏对物理内存的了解,由于缺乏物理内存管理相关的知识,我们对于内存管理没有形成体系,所以才会似懂非懂。
0a1tahs5cm364012509445.png
本文我们抛弃八股文的学习方式,以一种创新的方式来学习Linux内存管理,我们基于物理地址来学习Linux内存管理。
1.UMA和NUMA架构
UMA(Uniform Memory Access)和NUMA(Non-Uniform Memory Access)是两种不同的内存访问架构。
1.1 UMA架构
UMA是一种对称多处理(SMP)系统的内存访问架构,它指的是所有处理器对内存的访问具有相同的延迟。
在UMA系统中,所有处理器共享同一总线或交叉点,可以直接访问共享内存,这意味着无论哪个处理器访问内存,延迟都是相同的,UMA适用于小规模的多处理器系统。
y0wgehy5uzc64012509545.png
1.2 NUMA架构
NUMA是一种非对称多处理(NUMA)系统的内存访问架构,它指的是不同处理器对内存的访问延迟可能不同。
在NUMA系统中,每个处理器都有自己的本地内存和本地内存控制器,可以更快地访问本地内存。
如果一个处理器要访问其他处理器的本地内存,需要通过QPI总线去访问,这样延迟就会增加。
NUMA适用于大规模的多处理器系统,NUMA架构将物理内存分为一个个内存节点,内存节点和内存节点之间的物理地址有可能是不连续的,同一内存节点物理内存物理地址是连续的。
azf3tofqdbl64012509645.png
小节:
不管是UMA和NUMA架构,物理内存的物理地址都是唯一的。UMA架构可以理解为NUMA架构的一个节点,是NUMA架构的一种特殊情况。
2. 物理内存管理
我们围绕物理地址来理解物理内存,物理内存管理其实是对物理地址的管理。
2.1物理地址
物理地址是物理内存中每个字节的身份信息,我们要对物理内存进行读写必须要知道物理地址,每块物理内存区域通过起始地址和大小表示,每个物理地址的用途从硬件诞生就已经确定下来,如下图:
rs32baimazy64012509745.png
备注:图片来自《奔跑吧Linux内核卷1》
我们明确了物理地址的作用后,Linux内存管理可以简化为物理地址管理,如何管理物理地址,我们将用到页机制。
2.2 page有什么用?
页机制的核心是页(page),page为物理内存管理的最小单位。
如果每个物理地址我们都花精力去管理,管理成本会非常高,也非常不现实,所以需要一种高效且灵活的机制来管理物理地址,这种机制我们称为页机制。
ehipmh4eetc64012509845.png
如上图,物理内存被划分成一个个固定大小的页,页的大小通常为4KB,Linux通过struct page结构体来表示一个物理页,struct page定义如下:
zpzollk2gi464012509945.png
struct page实际大小大概为40B,并没有4KB大小,所以struct page只是物理页的映射和描述,并不是实际的物理页。
每一个物理页都有一个对应的页号,称为PFN,PFN和物理地址一样,同样是唯一的,通过PFN我们可以定位到物理页的首地址。
2.3 page,PFN,物理地址之间的关系?
(1)PFN和物理地址相互转换
PFN和物理地址遵循一定的转换关系,转换公式如下:
z11qusnmhob64012510046.png
PFN转物理地址
一个PFN对应4KB的内存,4KB为4096 Byte,PFN * 4096为PFN对应的物理页的首地址(物理地址),PFN * 4096 = PFN
物理地址转PFN
PFN = 物理地址 / 4096 = 物理地址 >> 12。
(2)page和PFN相互转换
page和PFN相互转换需要结合内存模型讨论。
(3)page和物理地址转换
page和物理地址转换,需要借助PFN完成,page和物理地址不能直接转换,需要先转换成PFN,再通过PFN完成转换。
3.三种内存模型
Linux内存模型分为:
平坦内存模型(FLATMEM)非连续内存模型(DISCONTIGMEM)稀疏内存模型(SPARSEMEM)
我们不要被这些高大上的名字吓住,其实这些内存模型都是通过数组实现,不同的内存模型对page的组织方式不同。
3.1 平坦内存模型
平坦内存模型是指在Linux操作系统中,物理内存被视为一个连续的、线性的地址空间,而不是分段或分页的方式,这种模型使得内存管理更加简单和高效。
etzcxkpnn3l64012510146.png
平坦内存模式采用struct pglist_data指针数组表示,每个数组元素都是一个struct pglist_data对象,struct pglist_data对象表示一个内存节点,内存节点就是我们前面讲到的NUMA架构内存节点,struct pglist_data对象有一个node_mem_map成员,该成员是一个struct page指针,指向一个页表。
平坦内存模型page和PFN转换:
yyx0udtrvrt64012510246.png
平坦内存模式只有一个节点0,平坦内存模型物理起始地址偏移为0。
PFN转page
PFN转page首先得找到页表起始地址,计算方式如下:
struct page *mem_map = node_data[0]->node_mem_map;
PFN和ARCH_PFN_OFFSET的差值为数组索引值,页表起始地址+数组索引值得到page地址。
page转PFN
page地址和mem_map地址的差值为数组索引值,数组索引值+ARCH_PFN_OFFSET为PFN值。
3.2 非连续内存模型
不连续内存模型是指内存空间被分割成多个不连续的区域,每个区域具有不同的物理地址范围。
不连续内存模型可以提供更灵活的内存管理方式,但也增加了程序设计和实现的复杂性。
mha1o31oeyv64012510346.png
非连续内存模型和平坦内存模型都是采用struct pglist_data指针数组来实现,非连续内存模型通常来说有多个节点,每个节点之间的物理内存的物理地址不连续,平坦内存模型可以理解为一个节点的非连续内存模型。
非连续内存模型和平坦内存模型page和PFN转换方式是一样的。
非连续内存模型的mem_map和ARCH_PFN_OFFSET每个节点都不一样。
3.3 稀疏内存模型
稀疏内存模型是一种用于管理大容量内存的机制,它允许系统在物理内存不连续的情况下有效地使用内存空间。稀疏内存模型中,内存被划分为多个区域,每个区域可以包含不同数量的页框。
稀疏内存模型的主要目的是解决内存碎片化的问题。在传统的连续内存模型中,当系统中有大量空闲页框但它们不连续时,无法有效利用这些内存空间,而稀疏内存模型通过将内存划分为多个区域,可以更灵活地管理内存碎片,从而提高内存利用率。
稀疏内存模型的管理单位为mem_section,定义如下:
es5d2th3cm164012510446.png
section_mem_map为页表起始地址,一个struct mem_section代表一块连续的物理内存区域,每个mem_section都有一个对应的编号,通过section编号可以定位到对应的mem_section。
5uvvl4zesc164012510546.png
稀疏内存模型page和PFN转换方式:
5pfe1mjek1m64012510646.png
page转PFN
struct page结构体flags成员记录了page的section编号,通过flags解析出section编号,再通过section编号定位到mem_section对象,mem_section对象成员section_mem_map为页表地址,page地址和section_mem_map之间的差值为PFN。PFN转page
通过PFN获取到section编号,通过section编号定位到mem_section对象,section_mem_map + PFN获取到page地址。
4.总结
Linux内存管理非常复杂,我们要掌握Linux内存管理,首先得了解页机制,通过页机制了解页,PFN,物理地址之间的关系,有了这个基础,我们再去学习Linux内存管理,我们的思路才会清晰。 |