内存管理-物理内存管理
什么是物理内存?内存硬件结构长什么样?物理内存模型和物理内存架构分别指的是什么?有什么区别和联系?
1 硬件结构
- 主存:
- 若干个存储器模块
- 每个存储器模块包含8个DRAM芯片(64位)
- 每个DRAM芯片上存储一个字节,8个DRAM组成一个64位的数据;
- 存储控制器
- 地址线(2个地址引脚:行地址+列地址)
- 数据线(8个data引脚:每个单元存储一个字节)
- 若干个存储器模块
- 访问流程:
- 存储控制器将行地址通过地址引脚发送给DRAM芯片
- DRAM芯片根据行地址将二维矩阵中的该行的全部内存全部拷贝到内部缓冲区中;
- 存储控制器通过地址引脚发送列地址到DRAM芯片;
- DRAM芯片从内部缓冲区根据列地址拷贝出对应存储单元数据并通过数据引脚发送给存储控制器;
2 CPU从内存读取数据过程
flowchart LR
总线接口 --> |系统总线|B[IO Bridge] --> |存储总线|C[主存]
2.1 大致流程
- CPU将物理内存地址作为地址信号放到系统总线上传输,IO Bridge将系统总线上的地址信号转换为存储总线上的地址信号;
- 贮存感受到存储总线上的地址信号,通过存储控制器将存储总线上的物理内存地址读出来;
- 存储控制器通过物理内存地址定位到具体的存储器模块,从DRAM芯片中去除物理内存地址对应的数据;
- 存储控制器将读取到的数据放到存储总线上,IO Bridge将存储总线上的数据信号转换为系统总线上的数据信号,然后通过系统总线传递;
- CPU芯片感受到总线上的数据信号,将数据从系统总线上读取出来并拷贝的寄存器中;
2.2 详细流程
3 物理内存模型
存在三种物理内存模型,FLATMEM平坦内存模型,DISCONTMEM非连续内存模型,SPARSEMEM稀疏内存模型;现代操作系统多使用第三种(稀疏内存模型);不同的内存模型对应着不同的物理页表项和物理页之间的转换关系;
- 物理内存被划分为若干个以4KB大小为单元的页,每个页使用一个struct_page结构体进行管理;
- struct_page结构体中封装了没页内存块的状态信息,使用情况以及映射关系;
- 为了快速定位到具体的页,内核为每个物理页struct_page结构体定义了一个索引编号:PFN
- PFN和struct_page是一一对应的关系,二者可以互相转换(不同的物理内存模型,对应的函数计算方式不同);
- page_to_fpn
- fpn_to_page
- 内核组织和管理物理内存页struct_page的方式被称为物理内存模型;
3.1 FLATMEM 平坦内存模型
- 将物理内存视为连续的线性物理空间,使用全局数组mem_map来管理所有内存页,数组下标就是相应物理页对应的PFN;
- FLATMEM平坦内存模型只适用于管理一整块连续的物理内存模型;
- 对于多块非连续的物理内存来说,使用该内存模型会造成很大的内存空间浪费;
物理内存不应该都是连续可用的吗?为什么说不是连续的?为什么会存在空洞?
- 为什么存在空洞:
- 某些物理内存地址可能会被硬件保留(比如BIOS,设备寄存器映射),导致操作系统无法使用这些区域;
- NUMA架构中,不同CPU节点的本地内存可能因为访问延迟差异形成逻辑上的“空洞”;
- 操作系统为安全隔离内核和用户空间,会预留部分内存,形成不可用的空间;
- 为什么不连续:
- 程序频繁申请和释放不同大小的内存块,导致外部碎片,例如伙伴系统中,释放的8KB内存可能被拆分成两个4KB块,无法合并原连续块;
- 现代操作系统通过分页管理内存,程序看到的连续虚拟地址可能映射到不连续的物理页;
- 页置换算法(LRU)可能将活跃页面分散到不同物理位置,加剧不连续性;
3.2 DISCONTIGMEM 非连续内存模型
- 内核将物理内存从宏观上划分成了一个一个的节点node,微观上还是一页一页的物理页,每个node节点管理一块连续的物理内存,节点内使用FLATMEM管理连续内存,从而避免空洞浪费。
- DISCONTIGMEM非连续内存模型是FLATMEM平坦内存模型的一种扩展,在进行大块不连续的物理内存管理时,通过将每段连续的物理内存区间划归到node节点中进行管理,避免为内存地址空洞分配struct page结构,从而节省内存资源开销;
- 相比FLATMEM平坦内存模型,多了两个函数:
- arch_pfn_to_nid:根据物理页的PFN定位到物理页所在的node;
- page_to_nid:根据物理页的struct page定位page所在的node;
3.3 SPARSEMEM 稀疏内存模型
- 核心思想:对粒度更小的连续内存块进行精细的管理;
- section被用来管理小粒度的连续内存块,这些小的连续物理内存在section中通过数组的方式被组织和管理;
- mem_section:指向section数组0元素,每个mem_section包含一个section_mem_map;
- section_mem_map:指向连续物理内存页表项第一个,每个页表项对应一个struct page;
- 每个struct page对应一个page frame;
- page_to_pfn:
- 先通过page_to_section根据struct page结构定位到mem_section数组中具体的section结构;
- 再通过section_mem_map定位到具体夫人PFN;
- pfn_to_page:
- 先通过__pfn_to_section根据PFN定位到mem_section数组中具体的section结构;
- 再通过PFN在section_mem_map数组中定位到具体的物理页;
3.4 三种内存模型对比
3.5 物理内存热拔插时发生了什么?
物理内存拔出时,被拔出的物理内存中可能已经为进程分配了物理页,如何迁移这些已经被分配的物理页?如何保证迁移后的物理页映射的虚拟内存地址不改变?哪些物理页是不能迁移的?
当物理内存拔出时,内核会执行以下步骤:
- 隔离内存区域:通过mem_section的offline状态标记内存为不可用;
- 迁移已分配页:将待拔出内存中的已分配页迁移到其他在线内存区域;
- 虚拟地址一致性:修改页表映射,确保迁移后进程的虚拟地址不变;
哪些物理页不能迁移:
- 内核虚拟地址中有一段直接映射区,这一段虚拟内存和物理地址线性相关,这类页迁移后会导致虚拟地址变化,因此不能迁移。
4 物理内存架构
物理内存架构分为UMA一致性内存访问架构和NUMA非一致性内存访问架构;UMA与NUMA均描述的是多个CPU与内存之间的架构关系;
4.1 UMA架构
CPU(多个)和内存条(多个)分别位于总线的两侧;所有CPU访问内存都需要经过总线,并且距离相同(所有CPU访问内存的速度是一样的);
- 随着CPU个数的增加,总线的带宽压力会越来越大,导致每个CPU可用带宽会减少;
- 随着CPU个数的总价,总线的长度也会因此而增加,进而增加访问延迟;
4.2 NUMA架构
在NUMA架构下,内存被划分成一个个的内存节点,每个CPU都有属于自己的本地内存节点;
- CPU访问自己的本地内存不需要经过总线,访问速度最快;
- 当CPU本地内存不足时,需要跨节点访问其他内存节点,此时访问速度会慢很多;
- 这里的CPU指的不是CPU核心,单个CPU可能包含若干个CPU核心,这里每个节点包含一个CPU(若干CPU核心);
4.3 NUMA相关命令
numactl -H:查看服务器的NUMA配置,服务器有几个节点,每个节点有几个CPU核心,节点与节点之间的距离;numactl -s:查看NUMA的内存分配策略;numastat:查看各个NUMA节点的内存访问命中率;
5 NUMA节点如何管理
- 内核2.4之前的版本:
- 使用pgdat_list单链表将NUMA节点串联起来;
- 内核2.4之后的版本:
- 使用一个大小为NAX_NUMNODES类型为struct pglist_data的全局数组node_data来管理所有的NUMA节点;
- struct pglist_data包含:
- 当前节点内所有物理页的首页地址;
- 指向NUMA节点内第一个物理页的PFN;
- 用于统计该节点内所有真正可用的物理页面数量(不包含空洞);
- 用于统计该节点内所有的内存页(包含空洞);
- NUMA节点中内存规整与回收
- 内核会为每个NUMA节点分配一个kswapd进程用于回收不经常使用的页面;
- 内核也会为每个NUMA节点分配一个kcompactd进程用于内存的规整避免内存碎片;
- struct zone
- 用于描述和管理NUMA节点中的物理内存区域;
- pglist_data通过struct zone类型的数组node_zones将NUMA节点中划分的物理内存区域连接起来;
- 这些物理内存区域也会通过struct zone中的zone_pgdat指向自己所属的NUMA节点;
物理内存中为什么要预留内存?
内核中关于内存分配的场景有两种:
- 允许阻塞:当进程请求内核分配内存时,如果内存充裕,那么会立即给进程分配内存;如果内存比较紧张,内核会将一部分不经常使用的内存进行回收,以满足内存分配的需求,这个回收过程是阻塞的,所以进程需要阻塞等待;
- 不允许阻塞:当进程请求内核分配内存时,需要立马进行内存分配,不能阻塞等待;比如执行中断处理程序的时候,进程不允许进入睡眠,因为中断程序不能被重新调度,这个时候就需要内核提前为这种情况预留一部分内存,即便是内存紧张时,当申请内存时,也能立即分配;
水位线作用
- 水位线的作用:用于衡量内存紧张程度,从而有触发不同的内存管理机制;
- kswapd异步内存回收
- 直接内存回收
- 内核为每个NUMA节点中的每个物理内存区域设置了三条用于指示内存容量的水位线:
- WMARK_MIN(页最小阈值)
- WMARK_LOW(页低阈值)
- WMARK_HIGH(页高阈值)
水位线计算逻辑
冷热页作用
冷热页如何管理
内核如何描述物理内存页
在NUMA框架下,内存被分成了一个个内存节点,每个NUMA节点中,内核根据物理内存的功能和用途,将NUMA节点内的物理内存划分为四个物理内存区域:ZONE_DMA,ZONE_DMA32,ZONE_NORMAL,ZONE_HIGHMEM。
物理内存区域中管理的是物理内存页,物理内存页是Linux内存管理的最小单位;内核会为每一个物理内存区域分配一个伙伴系统,用于管理该物理内存区域下所有物理内存页面的分配和释放;
为什么选4K作为标准物理内存页的大小?
匿名页反向映射是什么?
- 正向映射:从虚拟内存到物理内存的映射;
- 反向映射:从物理内存到虚拟内存的映射;
当某个物理内存页需要进行回收或迁移的时候,需要去找到这个物理页被映射到哪些进程的虚拟地址空间中,并断开他们之间的映射;
匿名页反向映射流程
- 在do_anonymous_page中,调用anon_vma_prepare方法为匿名页创建anon_vma实例和anon_vma_chain实例,并建立二者关联关系;
- 在anon_vma_prepare中,调用anon_vma_chain_link方法建立anon_vma,anon_vma_chain,vm_area_struct三者之间的关系;
- 调用alloc_zeroed_user_highpage_movable方法从伙伴系统中申请一个匿名页,获取page实例;
- 通过page_add_new_anon_rmap建立page到mv_area_struct的反向映射链路;