UMA(一致内存访问,uniformmemory access): 计算将内存以连续的方式组织起来。SMP中每个cpu访问各内存区具有一样的速度
NUMA(非一致内存访问,non-uniformmemory access):SMP中的各个cpu都有本地的内存,可支持快速访问。各个cpu之间通过总线连接,对其它cpu的内存的访问将会慢于对自己内存的访问。结构如下图:
在内核中内存划分为节点,每个节点关联到一个cpu,在内核中用pg_data_t表示。
各个节点进一步划分为域,在内核中用structzone来表示。对于x86来说,总共有ZONE_DMA,ZONE_NORMAL,ZONE_HIGMENT三个域实例。ZONE_DMA负责对负责分配用于DMA操作的内存,ZONE_NORMAL负责分配可以直接映射到内核段的内存,ZONE_HIGMENT负责分配超出内核段的物理内存(应该是指另外的3GB内存)。
注意:内存域这个概念是对于物理内存而言的,而不是虚拟内存
每个域中会包含一个structfree_area成员,该成员用于实现伙伴系统,管理该区域中的空闲内存页。
页在内核中对应的结构体为structpage,该结构体采用union语法来减少结构体的大小。Page中的mapping指定了页帧所在的地址空间,virtual用于存储该页的虚拟地址,在x86体系中virtual成员是没有的。
上图展示了节点,域,页之间的关系。这里再对每个概念所涉及功能进一步阐述(这里只是简单介绍部分功能,详细信息请参考《深入linux内核架构》3.2节):
//节点(简化版) typedef struct pglist_data { struct zone node_zones[MAX_NR_ZONES];//指明该节点下包含有多少个域 //指向page实例数组的指针,用于描述节点的所有物理内存页,它包含了节点中所有内存域的页 struct page *node_mem_map; //备用节点的内存域的链表,用于在NUMA中,该节点无法提供内存,需要到其它的节点分配 struct zonelist node_zonelists[MAX_ZONELISTS]; struct bootmem_data *bdata;//用于为分配在系统初始化之前所需要的内存? }pg_data_t; //域(简化版,并没有考虑NUMA) struct zone { unsigned long watermark[NR_WMARK];//系统在分配页之后,需要保证剩余下了的页的数量大于水印 //当空闲页数低于该值时,读取空闲页将会导致附加的操作被执行,而这一附加的操作是为了躲避能让水印被违反的per-cpucounter drift。 unsigned long percpu_drift_mark; //分别为各个内存域指定若干的保留页,用于系统的紧急内存分配 unsigned long lowmem_reserve[MAX_NR_ZONES]; struct per_cpu_pageset pageset[NR_CPUS];//用于存放冷热页,在高速缓存中的页称为热页。 struct free_area free_area[MAX_ORDER];//用于实现伙伴系统 spinlock_t lock;//自旋锁 ZONE_PADDING(_pad1_)//用于实现将每个自旋锁放置于不同的缓存行,x86中每个缓存行为32Bytes //以下两个成员用于页面回收程序使用 spinlock_t lru_lock; struct zone_lru { struct list_head list; }lru[NR_LRU_LISTS];//分别保存活动页和非活动页。页访问频繁及认为为活动页 struct zone_reclaim_stat reclaim_stat; unsigned long pages_scanned; /* since last reclaim */ unsigned long flags;// 用于描述内存域所处的状态,比如被LOCKED,所有的不能回收 atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];//用于保存对该域的统计数据,如活动页的数目 ZONE_PADDING(_pad2_) //以下三个字段,实现进程等待某一页 wait_queue_head_t *wait_table; unsigned long wait_table_hash_nr_entries; unsigned long wait_table_bits; struct pglist_data *zone_pgdat;//关联到自己所属的节点 unsigned long zone_start_pfn;// 内存域的第一个页帧 unsigned long spanned_pages; //总共的长度,包含空洞 unsigned long present_pages; //内存数量(不包含空洞) const char *name;//域的名字,内核将很少使用的字段放到结构体的末尾 }____cacheline_internodealigned_in_smp; //页(简化版),slab,freelist,inuse用于实现slub struct page { unsigned long flags ;//记录该页的状态信息,如该页是否被LOCKED,是否脏等等 atomic_t_count;//使用计数,表示内核中引用该页的次数 union { atomic_t_mapcount;// _mapcount为32位,表示在页表中多少项指向该页 struct { u16inuse;//用于slub分配器,对象的数目,该成员不需要原子特性 u16objects; }; }; union { struct { //一个指向私有数据的指针,根据页的用途,可以用不同的方式使用该指针 unsigned longprivate; //指定页帧所在的地址空间,index是页帧在内部的偏移量。当mapping最低位置1时该指针并不指向address_space实例,而是指向anon_vma struct address_space *mapping; }; struct kmem_cache *slab;//用于SLUB分配器,指向slab的指针 struct page *first_page;//指向compoundpage中的首页 }; union { pgoff_tindex;/* Our offset within mapping. */ void *freelist;/* SLUB: freelist req. slablock */ }; struct list_head lru;//用于将页按不同的类别分组,如活动和不活动 #ifdefined(WANT_PAGE_VIRTUAL) void*virtual; //在x86体系中,没有该成员 #endif/*WANT_PAGE_VIRTUAL */