内存管理关键数据结构 1.结点管理 pg_data_t是用于表示结点的基本元素,定义如下: 1.node_zone是一个数组,包含了结点各内存域的数据结构 2.node_zonelists指定了备用结点以及内存域的列表,以便在当前结点没有内存时,从备用结点分配内存。 3.结点中不同内存域的数目保存在nr_zones. 4.node_mem_map是指向page实例数组的指针,用于描述结点内所有物理内存页。它包含了结点内所有内存域的。 5.在系统启动期间,内存管理子系统初始化之前,内核页需要使用内存(另外还必须保留部分内存用于初始化内存管理子系统)。为解决这个问题,内核使用了自举内存分配器( boot memory allocator)。bdata是指向自举内存分配器数据结构的实例。 6.node_start_pfn是该NUMA结点第一个页帧的逻辑编号。系统中所有结点的页帧是依次编号的,每个页帧的编号都是全局唯一的。 node_start_pfn在UMA系统中总是0,因为其中只有一个结点,因此其第一个页帧编号总是0。 node_present_pages指定了结点中页帧的数目,而node_spanned_pages则给出了该节点以页帧为单位计算的长度。二者的值可能不同,因为结点中可能存在一些空洞,并不真正的对应页帧。 7.node_id,全局结点ID。系统中NUAM结点都从0开始编号。 8.pgdat_next,链接下一个结点,系统中所有结点都通过单链表连接起来,其末尾通过空指针标记。 9.kswapd_wait是交换守护进程的等待队列,在将页帧换出结点时会用到。kawapd指向负责该结点的交换守护进程的task_struct。kswapd_max_order用于页交换子系统的实现,用来定义需要释放的区域的长度(我们当前不感兴趣时)。 结点状态管理 如果系统中结点 多于一个,内核会维护一个位图,用以提供各个结点的状态信息,状态位是用掩码标识的,可以使用下列值。 对内存管理有必要的标志是N_HIGH_MEMORY和N_NORMAL_MEMORY。如果结点有普通或高端内存则使用N_HIGH_MEMORY,仅当结点没有高端内存才设置N_NORMAL_MEMORY。 两个辅助函数用来设置或清除位域或特定结点中的一个比特位: <nodemask.h> void node_set_state(int node, enum node_states state) void node_clear_state(int node, enum node_states state) 此外,宏for_each_node_state(__node, __state)用来迭代处于特定状态的所有结点,而foreach_online_node(node)则迭代所有活动结点。如果内核编译为只支持单个结点(即使用平坦内存模型),则没有结点位图,上述操作该位图的函数则变为空操作。 2.内存域 内核使用zone结构来描述内存域,其定义如下:
struct zone {
unsigned long _watermark[NR_WMARK];
unsigned long watermark_boost;
unsigned long nr_reserved_highatomic;
long lowmem_reserve[MAX_NR_ZONES];
#ifdef CONFIG_NUMA
int node;
#endif
struct pglist_data *zone_pgdat;
struct per_cpu_pageset __percpu *pageset;
#ifndef CONFIG_SPARSEMEM
unsigned long *pageblock_flags;
#endif
unsigned long zone_start_pfn;
atomic_long_t managed_pages;
unsigned long spanned_pages;
unsigned long present_pages;
const char *name;
#ifdef CONFIG_MEMORY_ISOLATION
unsigned long nr_isolate_pageblock;
#endif
#ifdef CONFIG_MEMORY_HOTPLUG
seqlock_t span_seqlock;
#endif
int initialized;
ZONE_PADDING(_pad1_)
struct free_area free_area[MAX_ORDER];
unsigned long flags;
spinlock_t lock;
ZONE_PADDING(_pad2_)
unsigned long percpu_drift_mark;
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
unsigned long compact_cached_free_pfn;
unsigned long compact_cached_migrate_pfn[ASYNC_AND_SYNC];
unsigned long compact_init_migrate_pfn;
unsigned long compact_init_free_pfn;
#endif
#ifdef CONFIG_COMPACTION
unsigned int compact_considered;
unsigned int compact_defer_shift;
int compact_order_failed;
#endif
#if defined CONFIG_COMPACTION || defined CONFIG_CMA
bool compact_blockskip_flush;
#endif
bool contiguous;
ZONE_PADDING(_pad3_)
atomic_long_t vm_stat[NR_VM_ZONE_STAT_ITEMS];
atomic_long_t vm_numa_stat[NR_VM_NUMA_STAT_ITEMS];
} ____cacheline_internodealigned_in_smp;
该结构特殊的方面在于它由ZONE_PADDING分割为几个部分。这是因为对zone结构的访问非常频繁。在多处理器系统上,通常有多个CPU访问结构成员。因此会使用锁防止它们彼此干扰,避免错误和不一致。由于内核对该数据结构访问非常频繁,因此会经常性的获取该结构的两个自旋锁zone->lock和zone->lrulock。
如果数据保存在CPU高速缓存中,那么会处理的更快速。高速缓存分为行,每一行负责不同的内存区。内核使用ZONE_PADDING宏生成填充字段添加到结构中,以确保每个自旋锁都处于自身的缓存行中
|