3.5 物理内存的管理--伙伴系统
在内核初始化完成以后,内存管理的责任交给伙伴系统承担。伙伴系统基于一种相对简单而令人吃惊的强大算法,已经出现40年。它结合了优秀内存分配器的两个关键特征:速度和效率。
3.5.1 伙伴系统的数据结构
系统内存中的每个物理内存页(页帧),都对应一个struct page实例。每个内存域都关联了一个struct zone的实例,其中保存了用于管理伙伴数据的主要数组。
struct zone {
...
/* free areas of different sizes */
struct free_area free_area[MAX_ORDER];
...
} ____cacheline_internodealigned_in_smp;
free_area是一个辅助数据结构,其定义如下:
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
nr_free指定了当前内存区中空闲页块的数目(对0阶内存区主页计算,对1阶内存计算页对的数目,对2阶内存区计算4页集合的数目,依次类推)。
free_list是用于连接空闲页的链表。页链表包含大小相同的连续内存区。
阶是伙伴系统中的重要术语,它描述了内存分配的数量单位。内存块的长度是2^order,其中order的范围从0到MAX_ORDER.
/* Free memory management - zoned buddy allocator. */
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif
#define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1))
该常数通常设置为11,这意味着一次分配可以请求的页数最大是2^11=2048 pages(2K*4K=8MBytes)。如果特定于体系结构的代码设置了FORCE_MAX_ZONEORDER配置选项,该值也可以手工改变。例如,IA-64系统上巨大的地址空间可以处理MAX_ORDER=18的情形,而ARM或v850系统则使用更小的值,如8或9,但这不一定是由计算机支持的内存数量比较小引起的,也可能是内存对齐方式的要求所导致。
free_area[]数组中各个元素的索引页解释为阶,用于指定对应链表中的连续内存区包含多少个页帧。第0个链表包含的内存区为单页(2^0=1),第一个链表管理的内存区为两页(2^1=2),第3个管理的内存区为4页,依次类推。
内存区是如何连接的?内存区中第1页的链表元素,可用于将内存区维持在链表中。因此,也不必引入新的数据来管理物理内存上的连续的页,否则这些页不可能在同一内存区中。图3-22给出了图示。
伙伴不必是连接的。如果一个内存区在分配期间分解为两半,内核会自动将未用的一半加入到对应的链表中。如果在未来的某个时刻,由于内存释放的缘故,两个内存区都处于空闲状态,可通过判断其是否为伙伴。管理工作较少,是伙伴系统的一个优点。
基于伙伴系统的内存管理专注于某个结点的某个内存域,例如,DMA或高端内存域。但所有内存域和结点的伙伴系统都通过备用分配列表连接起来,如图3-23所示。
在首选的内存域结点无法满足内存分配请求时,首先尝试同一节点的另一个内存域,接下来尝试另一个结点,直至满足请求。
最后要注意,有关伙伴系统当前状态的信息可以通过/proc/buddyinfo获取,也可以通过echo m >/proc/sysrq-trigger获取:
root@linux:/study/linux-git/linux-3.18.3# echo m >/proc/sysrq-trigger
[32594.375485] SysRq : Show Memory
..........
[32594.625700] DMA: 2*4kB (U) 1*8kB (E) 1*16kB (M) 2*32kB (EM) 2*64kB (EM) 1*128kB (E) 3*256kB (UEM) 3*512kB (EM) 1*1024kB (U) 1*2048kB (R) 0*4096kB = 5728kB
[32594.640749] Normal: 36*4kB (UEM) 11*8kB (UM) 12*16kB (UEM) 4*32kB (UEM) 0*64kB 2*128kB (EM) 7*256kB (UEM) 4*512kB (UM) 3*1024kB (UM) 8*2048kB (UER) 26*4096kB (UMR) = 130600kB
[32594.657634] HighMem: 2*4kB (UM) 18*8kB (UE) 53*16kB (UM) 57*32kB (UEM) 32*64kB (UEM) 15*128kB (UE) 9*256kB (UE) 8*512kB (UE) 7*1024kB (UE) 7*2048kB (UEM) 65*4096kB (UMR) = 300936kB
root@linux:/study/linux-git/linux-3.18.3#
root@linux:/study/linux-git/linux-3.18.3# cat /proc/buddyinfo
Node 0, zone DMA 2 1 1 2 2 1 3 3 1 1 0
Node 0, zone Normal 36 10 12 4 0 2 7 4 3 8 26
Node 0, zone HighMem 13 28 53 57 32 15 9 8 7 7 65
root@linux:/study/linux-git/linux-3.18.3#