内存架构
UMA :内存 具备统一结构可以统一 寻址
NUMA :存在多个内存节点和多个cpu 簇,假设有CPU 1 ,2,cpu2可以访问cpu 1 簇的内存节点,但是速度会比访问本地内存来的慢 (这个在某些嵌入式soc也会存在,多个cpu 不同内存域,并不是像网上说的服务器用,这个容易被误导)
参考文章:Linux内核内存管理:linux三种内存模型_sparse内存模型-CSDN博客
深入理解Linux内存管理(三)内存模型 - 知乎 (zhihu.com)
书籍 : 奔跑吧linux内核
linux内存模型
linux 存在三种内存模型 、
平坦内存模型 借一张网上图来说明
struct page数组mem_map,存储了连续的所有struct page结构体。给每一个page从0开始编号,把这个号码叫PFN
(page frame number,页帧号)。page到fpfn的转换关系如下:
__pfn_to_page
的实现
在平坦模型中,__pfn_to_page
宏通常通过将物理页帧号(PFN)乘以每个页描述符的大小(通常是 sizeof(struct page)
),然后加上页描述符数组的基地址,来计算得到对应页描述符的虚拟地址以下是一个示例实现:
#define __pfn_to_page(pfn) \ | |
(mem_map + ((pfn) - ARCH_PFN_OFFSET)) |
这里 mem_map
是页描述符数组的基地址,ARCH_PFN_OFFSET
是特定于架构的偏移量,用于将内核管理的物理页帧号转换为页描述符数组中的索引。这个偏移量通常用于跳过内核保留的某些页帧。
__page_to_pfn
的实现
相反,__page_to_pfn
宏则是将页描述符的虚拟地址减去页描述符数组的基地址,然后除以每个页描述符的大小,来得到物理页帧号。
#define __page_to_pfn(page) \ | |
(((unsigned long)(page) - PAGE_OFFSET) / sizeof(struct page)) |
这里 PAGE_OFFSET
是页描述符数组在虚拟地址空间中的起始地址。通过从页描述符的虚拟地址中减去这个起始地址,并除以页描述符的大小,就可以得到页描述符在数组中的索引,即物理页帧号。
Discontiguous Memory Model
非连续内存模型
存在内存空洞 基本不用了
多一个pglist_data来进行管理,NUMA结构存在多块内存,内核用node结点来表示一块内存,对应的数据结构是struct pglist_data,PFN到page的转换关系 :通过
PFN得到node id,然后根据这个id找到对应的pglist_data数据结构,从而也找到了对应的page数组
#define __pfn_to_page(pfn)\
({unsigned long __pfn = (pfn);\
unsigned long __nid = arch_pfn_to_nid(__pfn); \
NODE_DATA(__nid)->node_mem_map + arch_local_page_offset(__pfn, __nid);\
})
mem_map
是页描述符数组的基地址 + 通过pfn和前面获得的id得到的偏移值
Sparse Memory Model
稀松内存模型
理论上最大支持的物理内存,按照SECTION(可配置 64 128MB)分,各个section支持热插拔的,section单元管理内存online/offline,每进行一次热插拔,都会对相应的section进行调整,如设置一些标志,分配或回收section_memmap等
通过全局二维数组struct mem_section **mem_section来维护映射关系
mem_section
数组是通过指针数组来组织的,这个指针数组的每个元素指向一个 struct mem_section
的实例。这个指针数组的大小是由 SECTIONS_PER_ROOT
定义的,即一个物理页(PAGE_SIZE
)能容纳多少个 struct mem_section
的指针。但是,这个指针数组本身并不直接存储在物理页中;而是存储在内核的某个数据结构或内存区域中