《深入理解linux内核》中对内存管理的解读比较粗略,相比之下陈莉君的书要详细很多了。如果
有人想了解内存管理的细节,可以去看看陈莉君的书,确实非常的详细。
简单的记笔记没有什么意思,按自己的疑惑来记录吧!首先,内存管理是对全局内存进行管理的,
但是内存管理的数据也需要占内存,又是一个鸡生蛋、蛋生鸡的问题。
这个问题和内存页表建立的问题差不多的。先不谈页表吧!页表建立是在内存管理以后的。首先
知道一个东西,内存管理的一部分数据是在内核数据段或者初始段的。
怎么看呢?有一个System.map的文件,这是编译内核之后得到的内核符号表,这张表里面有内核
重要的全局数据结构,当然很多很多的函数。以前看到过这个文件,但是没有仔细研究。一看才知道
这个文件非常的有用,它里面记录了内核代码区、数据区、初始化区的布局图,变量及函数的地址也
尽在其中。由于内核代码太过庞大,反汇编实在不是一个可行的办法。这样System.map就有了大作用
。
c0923510 D contig_page_data //在data段
co16d598 T bootmem_node_data //在init段
c094ae38 B mem_map //在数据段
这个文件有4万多行,我截取几个和内存管理有关的看看。上面这几个变量就是内存初始化过程中
比较重要的几个变量。这几个重要的数据结构都存在于内核的映像中,这本来应该是很明显的。但是
这样一看,感觉心里更有底了。这个鸡和蛋的问题也就有了一点眉目。既然这几个数据都在内核映像
里面,内存管理的时候,我们只需要将内核映像区标记为已用,其他的事情就不需要管了。这样就下
面就对这几个变量进行分析。
这几个变量在内核中的定义如下:
/*mm/bootmem.c*/
struct pglist_data __refdata contig_page_data = {
.bdata = &bootmem_node_data[0]
};
bootmem_data_t bootmem_node_data[MAX_NUMNODES] __initdata;
bootmem_data_t类型的定义如下:
typedef struct bootmem_data {
unsigned long node_min_pfn;
unsigned long node_low_pfn;
void *node_bootmem_map;
unsigned long last_end_off;
unsigned long hint_idx;
struct list_head list;
} bootmem_data_t;
为了对页面管理机制作出初步准备,Linux 使用了一种叫 bootmem 分配器(Bootmem Allocator)的
机制,这种机制仅仅用在系统引导时,它为整个物理内存建立起一个页面位图。这个位图建立在从 start_pfn
开始的地方,也就是说,内核映像终点_end 上方的地方。这个位图用来管理低区(例如小于 896MB),
因为在 0 到 896MB 的范围内,有些页面可能保留,有些页面可能有空洞,因此,建立这个位图的目的就
是要搞清楚哪一些物理页面是可以动态分配的。bootmem_data_t结构体中的node_bootmem_map就是
一个指向位图的指针。node_boot_start 表示存放 bootmem 位图的第一个页面(即内核映像结束处的第
一个页面)。 node_low_pfn 表示物理内存的顶点,最高不超过896MB。
变量 contig_page_data的类型就是前面介绍过的 pg_data_t 数据结构。每个 pg_data_t数据结构代
表着一片均匀的、连续的内存空间。在连续空间 UMA 结构中,只有一个节点contig_page_data,而在
NUMA 结构或不连续空间 UMA 结构中,有多个这样的数据结构。pg_data_t 结构中有个指针 bdata,
bdata 被初始化为指向 bootmem_data_t。
pg_data_t类型的定义如下:
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES];
zonelist_t node_zonelists[GFP_ZONEMASK+1];
int nr_zones;
struct page *node_mem_map;
unsigned long *valid_addr_bitmap;
struct bootmem_data *bdata;
unsigned long node_start_paddr;
unsigned long node_start_mapnr;
unsigned long node_size;
int node_id;
struct pglist_data *node_next;
} pg_data_t;
每个结构中的 node_mem_map 指向具体节点的 page 结构数组。另外一个重要的变量就是剩下的
mem_page。变量 mem_map 是类型为 struct pages 的全局稀疏矩阵。我们应该知道每一个内存页面都
有一个page变量对应它。pg_data_t中的node_mem_map变量也是一个page数组,但是它只是全局矩阵
mem_page的一部分。因为Linux要支持NUMA结构。最后一张图结束吧!