目录
3.1 概述
3.2 NUMA模型的内存组织
3.2.1 概述
3.2.2 三个数据结构
3.2.2.1 node
3.2.2.2 zone
3.2.2.3 page
本专栏文章将有70篇左右,欢迎+关注,订阅后续文章。
本章讲物理内存的管理,而不是虚拟内存地址空间。
3.1 概述
页帧:指物理内存的一页,通常4K大小。
每页用一个struct page实例表示。
内存管理包含:
大块内存管理:伙伴系统
小块内存管理:slab slub slob kmalloc
物理非连续内存管理:vmalloc
物理内存页帧管理:struct page
进程地址空间
PAE机制:(Physical Address Extension,物理地址扩展)
作用:扩展了32位x86 CPU的物理内存寻址范围,允许访问超过2^32(4GB)的物理内存。
PAE实现原理:
1. 物理地址扩展:
地址总线从32位扩展到36位。可寻址64GB物理内存。
2. 页表结构改变:
PAE引入了更复杂的页表结构,以支持更大的物理地址范围。
注意:虽然内核可访问更大的物理内存地址,但线性地址仍是32位,所以单进程仍只能访问32位虚拟地址空间。
UMA:Uniform Memory Access
即一致内存访问。
含义:所有CPU共享同一内存,所有CPU访问该内存速度一样。
适用对象:小型系统。
NUMA:Non-Uniform Memory Access
含义:
每个CPU有自己的本地内存节点。
CPU访问本地内存节点比其他CPU的内存节点更快。
适用对象:大型系统。
个人电脑大部分采用UMA,而服务器通常采用NUMA。
SMT:同时多线程技术(超线程)
作用:
在一个CPU核中引入多个线程处理单元,当一个线程在等待某些资源时,可切换到另一个线程执行,提高整体性能。
缺点:
1. 资源竞争。因为多个线程共享同一CPU核资源。
2. CPU缓存抖动。因为多个线程共享cache。
DRAM:动态随机存储器,用作主存。
SRAM:静态随机存储器,用作CPU缓存。
三种内存组织类型:
1. FLATMEM:
即平坦内存模型。
最简单。整个物理内存视为一个连续区域。
缺点:只支持单个内存。
2. DISCONTIGMEM:
即非连续内存模型。
整个物理内存被分成多个不连续的区域,区域之间有空洞。每个区域属于一个内存结点。
场景:用于NUMA系统。
缺点:NUMA节点粒度太粗,不支持内存热插拔。
3. SPARSEMEM:
即稀疏内存模型。
某些区域连续,而另一些不连续。
场景:也用于NUMA系统。
在DISCONTIGMEM基础上优化。
DISCONTIGMEM和SPARSEMEM比较:
DISCONTIGMEM:更稳定,但不支持内存热拔插。
SPARSEMEM:不稳定,但支持内存热拔插。
相同:都支持NUMA。
总结:
UMA系统:适合FLATMEM。用于个人电脑。
NUMA系统:适合DISCONTIGMEM,SPARSEMEM。用于服务器。
物理内存页框号(PFN)与struct page互相转换:
struct page *pfn_to_page(unsigned long pfn)
unsigned long page_to_pfn(struct page *page)
内核中分配内存页时,分配单位为:order。
分配数量为2的order次方。
3.2 NUMA模型的内存组织
UMA:只有一个内存节点,所有CPU共享。
NUMA:每个CPU都有一个本地内存节点。
每个内存节点用一个 pg_data_t 实例表示。
3.2.1 概述
将物理内存分三级结构:
节点(node)、区域(zone)、页(page)
1. node:
UMA中只有一个内存node。而NUMA中每个CPU都有一个内存node。
配置选项:CONFIG_NUMA
2. zone:
根据用途和属性不同,每个内存node可分为多个zone。如:
enum zone_type {
ZONE_DMA,
ZONE_DMA32,
ZONE_NORMAL, 正常可寻址区域
ZONE_HIGHMEM, 高端内存区,64位系统不需要。
ZONE_MOVABLE, 伪区域,用于内存热插拔/迁移,防内存碎片
__MAX_NR_ZONES
};
ZONE_DMA:
用于DMA设备访问物理内存前16MB。
老式ISA DMA设备会使用。
ZONE_DMA32:
用于支持32位系统中DMA设备访问内存16MB到4GB范围。
现代DMA设备使用。
ZONE_HIGHMEM:
高端内存区。
问题:32位系统中虚拟内核空间只有1GB(范围3 - 4GB),无法映射访问全部4G的物理内存。
解决方法:使用128M虚拟内核空间,动态映射到物理内存的高端内存,用完解除映射。
ZONE_MOVABLE:
伪区域,用于内存热插拔,内存迁移,内存碎片整理。
3. page:
将物理内存分为一个个页帧,即page,通常一个page大小为4KB,每个zone包含一组page。
page是虚拟内存映射到物理内存的最小单位。
小结:
每个node包含多个zone。
每个zone包含多个page。
如下图:
系统调用
long migrate_pages(int pid, unsigned long maxnode,
unsigned long *old_nodes,
unsigned long *new_nodes);
作用:
将指定进程的页面从old内存节点迁移到new内存节点。
举例:
将进程的页面迁移到本地CPU内存节点上。
优点:
减少NUMA中内存访问延迟。
3.2.2 三个数据结构
本节讲node,zone,page三者数据结构。
3.2.2.1 node
node数据结构:pg_data_t。
每个NUMA节点都有一个pg_data_t实例。
系统所有node保存在pg_data_t node_data[nid];数组中。
typedef struct pglist_data {
struct zone node_zones[MAX_NR_ZONES];
//该节点所有的zone。
struct zonelist node_zonelists[MAX_ZONELISTS];
//用于快速查找特定zone,便于内存分配和回收操作。
int nr_zones;
//该节点中zone数量。
struct page *node_mem_map;
//该节点所有页。
struct bootmem_data *bdata;
//bootmem分配器,用于内核初始化时分配内存。
unsigned long node_start_pfn;
//该节点起始页帧号,所有节点统一编号。
//UMA只有一个节点,node_start_pfn为0。
unsigned long node_present_pages;
//该节点中物理页总数 。
unsigned long node_spanned_pages;
//该节点中物理页总数,包含空洞。
int node_id;
//全局节点ID
struct pglist_data *pgdat_next;
//下一个节点
struct task_struct *kswapd;
//该节点的页回收线程,当内存压力高时唤醒线程,执行内存回收。
int kswapd_max_order;
//kswapd线程一次可回收的最大页数,防止回收过多。
wait_queue_head_t kswapd_wait;
//其他线程在该等待队列上睡眠,等待kswapd线程完成内存回收。
wait_queue_head_t pfmemalloc_wait;
//当一个内核线程需分配内存以完成紧急任务(如中断处理),但内存不足,此时把线程挂起并加入到pfmemalloc_wait等待队列。
//当内存足够时,唤醒等待队列中线程,以继续分配内存。
struct task_struct *kcompactd;
//内存规整线程。内存碎片化达到一定程度时被唤醒,用于碎片整理。
wait_queue_head_t kcompactd_wait;
//其他线程在该等待队列上睡眠,等待kcompactd线程完成内存规整。
int kcompactd_max_order;
//kcompactd线程一次可规整允许最大页数。避免移动过多内存。
} pg_data_t;
成员介绍:
struct zonelist node_zonelists[MAX_ZONELISTS];
MAX_ZONELISTS= 2
[0]: 表示Zonelist with fallback
备用zone列表,按优先级先后包含所有节点的zonelist。
内存分配时,如果高优先级zone无法满足分配请求,会遍历下一个低优先级zone。
[1]: 表示No fallback
只包含本地节点的zone。
使用GFP_THISNODE标志分配内存时会遍历该node_zonelists中zone。
如何从指定内存结点中分配内存?
void *kmalloc_node(size_t size, gfp_t flags, int node)
void *vmalloc_node(size_t size, int node);
struct page *alloc_pages_node(int nid, gfp_t gfp_mask,
unsigned int order)
节点状态
enum node_states {
N_POSSIBLE,
N_ONLINE,
N_NORMAL_MEMORY, 该节点包含normal zone
N_HIGH_MEMORY, 该节点包含高端内存zone
N_CPU,
NR_NODE_STATES
};
N_POSSIBLE:
系统启动时,所有NUMA节点都会被标记为 N_POSSIBLE
表示节点已准备好进入下一个状态。
N_ONLINE:
表示节点已完全初始化并可供进程使用。
N_CPU:
节点是CPU的本地内存结点。
节点状态函数:
void node_set_state(int node, enum node_states state);
void node_clear_state(int node, enum node_states state);
3.2.2.2 zone
下节讲
3.2.2.3 page
下下节讲