概要:本文主要讲解了内存初始化所涉及到的三个函数
bootmem_init
paging_init
mem_init
第一节 内存管理概述
uclinux把整个连续物理地址空间看作是一个由许多物理页帧(Physical Page Frame)组成的"页帧数组",一般来说,页帧的大小则为4KB。
在文件linux/include/asm-arm/proc-armv/page.h中
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_SHIFT 12
因此每个物理页帧的起始物理地址的低12为必定全部为0,而它的高20位在右移12位后就是该页帧的序号,也就是该物理页帧在数组中的下标索引。
ucLinux在头文件include/linux/mm.h中定义了数据结构page来描述一个物理页帧:
typedef struct page {
struct list_head list; //链接入各种page list的结构
。。。。。。
} mem_map_t;
uclinux在系统初始化时根据系统物理内存的实际大小建立起page结构数组mem_map,每个物理页帧都在该数组中对应有一个成员来描述它,物理页帧的序号就是它在mem_map数组中的下标索引。 对于UMA来说,mem_map描述了所有的物理内存页面,
在传统的计算机系统中,整个物理地址空间中的存储器的属性都是一致的,比如CPU对任意物 理地址的访问速度都是一样的,因此这种结构也称为"匀质存储结构"(Uniform Memory A rchitecture,简称UMA)。 而在多CPU体系结构的计算机中,物理存储空间的属性却往往不尽相同。每个CPU都可能有自 己的本地存储器,系统中还可以有公用的共享存储器,这些存储器一起构成一个物理地址连 续的系统存储空间;每个CPU访问其本地存储器是最快的,而访问其他CPU节点的存储器或共 享存储器则较慢,因此这就使得不同物理地址处的存储器的属性可能不一样。这种存储结构 也就称为"非均质存储结构"(Non-Uniform Memory Architecture,简称NUMA)。在NUMA
结构的系统中,属于同一个CPU节点的存储器模块通常是匀质的,因此也被称为"存储节点" (Memory Node)。
Linux内核2.4.0版开始增加对NUMA的支持,内存区就不再是全局的最高层结
构,而是从属于某一个具体的存储节点。
ucLinux在头文件include/linux/mmzone.h中定义了数 据结构pglist_data来描述一个存储节点:
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES]; //3种分配区
zonelist_t node_zonelists[NR_GFPINDEX]; //256种分配类型
struct page *node_mem_map; // 分配域的页结构表
unsigned long *valid_addr_bitmap;
struct bootmem_data *bdata; //内核自举时所使用的分配域,大家在初始化的时候要极度注意该区域,在初始化的时候,是又改结构负责管理内存的使用d
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; //分配域结构
1. 若干存储节点的pglist_data数据结构通过指针node_next形成一个单向链接的存储节点队 列。不过一般的嵌入式的环境我估计也就一个这样的结构啦。
2. 系统仍然维护一个全局的page结构数组mem_map,但是每个存储节点都通过pglist_data结 构中的指针node_mem_map指向mem_map数组中属于该存储节点的起始物理页帧的page结构。
3. 数组node_zones[MAX_NR_ZONES]定义了属于该存储节点的内存区。
4. 数组node_zonelists[NR_GFPINDEX]定义了拥有该存储节点的CPU在分配连续的物理页帧块 时可以选择使用物理内存区。
显然,我们的板子暂时不支持努NUMA,但是内核的数据结构确实按照NUMA的方式构建的
在文件---linux/mmnommu/numa.c中定义了存储节点,显然,我们只有一个这样的结构。
static bootmem_data_t contig_bootmem_data;
pg_data_t contig_page_data = { bdata: &contig_bootmem_data };它是一个代表本节点的内存信息的一个数据结构
数据结构zonelist_struct的定义如下:
typedef struct zonelist_struct{
zone_t *zones[MAX_NR_ZONES+1];
unsigned long gfp_mask;
}zonelist_t;
显然,一个zonelist_struct结构为存储节点定义了一种可供选择的分配策略。也即当从一个 存储节点分配连续的物理页帧块时,可以从zones指针数组中寻找可满足此次分配需求的物理 内存区。
ucLinux在头文件include/linux/mmzone.h中定义了数 据结构zone_struct
typedef struct zone_struct {
spinlock_t lock;
unsigned long free_pages; //空闲页面的数量
unsigned long pages_min, pages_low, pages_high; //判断页面使用情况的阈值
int need_balance;
free_area_t free_area[MAX_ORDER]; //描述空闲页面
注意: uclinux通过zone_struct数据结构中一组"空闲区"(free area)队列来管理包含在其中的物理页帧,也即free_area[MAX_ORDER]数组。为什么是通过一组"空闲区"(free area)队列,而不是一个"空闲区"(free area)队列呢?这是因为我们常常需要成"块"地分配物理地址连续的多个物理页帧。因此,在zone_struct数据结构中即要有一个队列来保持一些离散(连续长度为1)的物理页帧,还要有其他多个队列来保持连续长度为2、4、8、16、…、2MAX_ORDER的页帧块。常数MAX_ORDER值为10,因此在Linux中最大的连续页帧块可以达到210=1024个页帧,即4M字节大小。
wait_queue_head_t * wait_table;
unsigned long wait_table_size;
unsigned long wait_table_shift;
struct pglist_data *zone_pgdat; //指向该内存区(zone)属于的节点信息。
struct page *zone_mem_map; //指向本zone的第一个page结构
unsigned long zone_start_paddr; //本zone开始的物理地址
unsigned long zone_start_mapnr; //用来表示该内存区所包含的物理页帧在page数组中的起始页帧序号
char *name;
unsigned long size;
} zone_t;
该数据结构描述了物理内存区的信息。为了更加方便地管理和使用物理地址空间,Linux将整个连续的物理地址空间划分为三段,每一段物理地址空间形成一个物理内存区(zone),分别是:ZONE_DMA区、ZONE_NORMAL区和ZONE_HIGHMEM区。其中,ZONE_DMA区用于管理低端的16M物理内存范围,这一段物理内存是转供DMA使用的。ZONE_HIGHMEM区用于管理高端的1GB以上的物理内存(如果有的话)。 每一个物理页帧根据其页序号(也即它的物理地址范围)决定它隶属于哪一个物理内存区,而且这种隶属关系是永久不变的。
在我们的板子上,共有三个zone,定义如下:
#define ZONE_DMA 0
#define ZONE_NORMAL 1
#define ZONE_HIGHMEM 2
#define MAX_NR_ZONES 3
但是实际上,所有的内存页都保存在了ZONE_NORMAL中
前面罗嗦了这么久,其实好大部分都是一些内核前辈的观点,这里罗列出来,以便使下面的分析会顺畅点
第二节 bootmem_init 和 paging_init
系统初始化时对内存的初始化源自下列代码(删掉了若干不相关代码)
void __init setup_arch(char **cmdline_p)
{
1 if (meminfo.nr_banks == 0) {
2 meminfo.nr_banks = 1;
3 meminfo.bank[0].start = PAGE_OFFSET;
4 meminfo.bank[0].size = MEM_SIZE;
5 }
6 bootmem_init(&meminfo); //初始化bootmem
7 paging_init(&meminfo, mdesc); // mem_map is set up here!
}
解释:
1------5行
在这里,我们设定meminfo结构,用此结构描述我们的内存基本情况,之后,会用该变量初始化contig_page_data的数据
#define PHYS_OFFSET (DRAM_BASE)
#define PAGE_OFFSET PHYS_OFFSET
具体的内存设置的值来自config.h文件,具体的板子具体分析
正常的内存管理应该由pg_data_t、 zone、 mem_map 等数据结构来管理,但此时这些结构还没有建立,bootmem是在初始化的时候用来描述内存使用情况。
void __init bootmem_init(struct meminfo *mi)
{
struct node_info node_info[NR_NODES], *np = node_info;
unsigned int bootmap_pages, bootmap_pfn, map_pg;
int node, initrd_node;
bootmap_pages = find_memend_and_nodes(mi, np);后面会详细分析这个函数,为了在启动阶段描述内存使用情况我们需要一些内存空间,这些空间叫做bootmem,此时bootmap_pages表明了bootmem所需要的pages的数目
bootmap_pfn = find_bootmap_pfn(0, mi, bootmap_pages); 后面会详细分析这个函数,通过这个函数,bootmap_pfn设置了bootmem所在的初始页号。也就是说从bootmap_pfn到bootmap_pfn + bootmap_pages的内存页被用来描述初始化的时候的内存的用用情况
initrd_node = check_initrd(mi);//俺们的板子没有用
map_pg = bootmap_pfn;
np += numnodes - 1;
初始化node结构
for (node = numnodes - 1; node >= 0; node--, np--) {
if (np->end == 0) {
if (node == 0)
BUG();
continue;
}
init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end); 后面会详细分析这个函数
free_bootmem_node_bank(node, mi);//释放所有内存,也就是把bootmem的区域全部设置为0
map_pg += np->bootmap_pages;
我们有可能会保留一些内存以便使值不能被动态分配,具体要保留什么内容,后面会详细分析
if (node == 0)
reserve_node_zero(bootmap_pfn, bootmap_pages);
}
if (map_pg != bootmap_pfn + bootmap_pages)
BUG();
}
void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
void *zero_page;
int node;
memcpy(&meminfo, mi, sizeof(meminfo));
分配一个页面的内存,很显然,我们刚才做的工作起了作用,我们会在表示该页面的那个bootmem中的bit设置值为1,以此表示该页面已经被分配出去拉
zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
unsigned long zone_size[MAX_NR_ZONES] = {0,0,0};
zone_size[ZONE_DMA] = 0;
zone_size[ZONE_NORMAL] = (END_MEM - PAGE_OFFSET) >> PAGE_SHIFT;
free_area_init_node(0, NULL, NULL, zone_size, PAGE_OFFSET, NULL);后面会详细分析这个函数
memzero(zero_page, PAGE_SIZE);
empty_zero_page = virt_to_page(zero_page);
flush_dcache_page(empty_zero_page);//抱歉,我也搞不太懂,应该是和cpu体系相关的
}
void __init free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap,
unsigned long *zones_size, unsigned long zone_start_paddr,
unsigned long *zholes_size)
{
free_area_init_core(0, &contig_page_data, &mem_map, zones_size,
zone_start_paddr, zholes_size, pmap);
}
/*
* Set up the zone data structures:
* - mark all pages reserved
* - mark all memory queues empty
* - clear the memory bitmaps
*/
void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
unsigned long *zones_size, unsigned long zone_start_paddr,
unsigned long *zholes_size, struct page *lmem_map)
{
unsigned long i, j;
unsigned long map_size;
unsigned long totalpages, offset, realtotalpages;
const unsigned long zone_required_alignment = 1UL << (MAX_ORDER-1);
if (zone_start_paddr & ~PAGE_MASK)
BUG();
totalpages = 0;
for (i = 0; i < MAX_NR_ZONES; i++) {
unsigned long size = zones_size ;
totalpages += size;
}
realtotalpages = totalpages;
if (zholes_size)
for (i = 0; i < MAX_NR_ZONES; i++)
realtotalpages -= zholes_size;
printk("On node %d totalpages: %lu/n", nid, realtotalpages);
/*
* Some architectures (with lots of mem and discontinous memory
* maps) have to search for a good mem_map area:
* For discontigmem, the conceptual mem map array starts from
* PAGE_OFFSET, we need to align the actual array onto a mem map
* boundary, so that MAP_NR works.
*/
给表示所有物理内存的page结构数组分配空间
map_size = (totalpages + 1)*sizeof(struct page);
if (lmem_map == (struct page *)0) {
lmem_map = (struct page *) alloc_bootmem_node(pgdat, map_size);
lmem_map = (struct page *)(PAGE_OFFSET +
MAP_ALIGN((unsigned long)lmem_map - PAGE_OFFSET));
}
*gmap = pgdat->node_mem_map = lmem_map;
pgdat->node_size = totalpages;
pgdat->node_start_paddr = zone_start_paddr;
pgdat->node_start_mapnr = (lmem_map - mem_map);
pgdat->nr_zones = 0;
offset = lmem_map - mem_map;
下面给zone_struct赋值
for (j = 0; j < MAX_NR_ZONES; j++) {
zone_t *zone = pgdat->node_zones + j;
unsigned long mask;
unsigned long size, realsize;
zone_table[nid * MAX_NR_ZONES + j] = zone;
realsize = size = zones_size[j];
if (zholes_size)
realsize -= zholes_size[j];
printk("zone(%lu): %lu pages./n", j, size);
zone->size = size;
zone->name = zone_names[j];
zone->lock = SPIN_LOCK_UNLOCKED;
zone->zone_pgdat = pgdat;
zone->free_pages = 0;
zone->need_balance = 0;
if (!size)
continue;
/*
* The per-page waitqueue mechanism uses hashed waitqueues
* per zone.
*/
zone->wait_table_size = wait_table_size(size);
zone->wait_table_shift =
BITS_PER_LONG - wait_table_bits(zone->wait_table_size);
zone->wait_table = (wait_queue_head_t *)
alloc_bootmem_node(pgdat, zone->wait_table_size
* sizeof(wait_queue_head_t));
for(i = 0; i < zone->wait_table_size; ++i)
init_waitqueue_head(zone->wait_table + i);
pgdat->nr_zones = j+1;
mask = (realsize / zone_balance_ratio[j]);
if (mask < zone_balance_min[j])
mask = zone_balance_min[j];
else if (mask > zone_balance_max[j])
mask = zone_balance_max[j];
zone->pages_min = mask;
zone->pages_low = mask*2;
zone->pages_high = mask*3;
zone->zone_mem_map = mem_map + offset;
zone->zone_start_mapnr = offset;
zone->zone_start_paddr = zone_start_paddr;
if ((zone_start_paddr >> PAGE_SHIFT) & (zone_required_alignment-1))
printk("BUG: wrong zone alignment, it will crash/n");
/*
* Initially all pages are reserved - free ones are freed
* up by free_all_bootmem() once the early boot process is
* done. Non-atomic initialization, single-pass.
*/
处理该zone_struct上的所有页面,具体含义由函数名可猜出
for (i = 0; i < size; i++) {
struct page *page = mem_map + offset + i;
set_page_zone(page, nid * MAX_NR_ZONES + j);
set_page_count(page, 0);
SetPageReserved(page);
INIT_LIST_HEAD(&page->list);
if (j != ZONE_HIGHMEM)
set_page_address(page, __va(zone_start_paddr));
zone_start_paddr += PAGE_SIZE;
}
//初始化zone->free_area
offset += size;
//为zone的buddy bitmap分配内存
for (i = 0; ; i++) {
unsigned long bitmap_size;
INIT_LIST_HEAD(&zone->free_area.free_list);
if (i == MAX_ORDER-1) {
zone->free_area.map = NULL;
break;
}
//求出buddy bitmap的字节数
bitmap_size = (size-1) >> (i+4);
bitmap_size = LONG_ALIGN(bitmap_size+1);
zone->free_area.map =
(unsigned long *) alloc_bootmem_node(pgdat, bitmap_size);
}
}
//构建页面分配的策略,也就是填充zonelists结构
build_zonelists(pgdat);
}
bootmem_init
paging_init
mem_init
第一节 内存管理概述
uclinux把整个连续物理地址空间看作是一个由许多物理页帧(Physical Page Frame)组成的"页帧数组",一般来说,页帧的大小则为4KB。
在文件linux/include/asm-arm/proc-armv/page.h中
#define PAGE_SIZE (1UL << PAGE_SHIFT)
#define PAGE_SHIFT 12
因此每个物理页帧的起始物理地址的低12为必定全部为0,而它的高20位在右移12位后就是该页帧的序号,也就是该物理页帧在数组中的下标索引。
ucLinux在头文件include/linux/mm.h中定义了数据结构page来描述一个物理页帧:
typedef struct page {
struct list_head list; //链接入各种page list的结构
。。。。。。
} mem_map_t;
uclinux在系统初始化时根据系统物理内存的实际大小建立起page结构数组mem_map,每个物理页帧都在该数组中对应有一个成员来描述它,物理页帧的序号就是它在mem_map数组中的下标索引。 对于UMA来说,mem_map描述了所有的物理内存页面,
在传统的计算机系统中,整个物理地址空间中的存储器的属性都是一致的,比如CPU对任意物 理地址的访问速度都是一样的,因此这种结构也称为"匀质存储结构"(Uniform Memory A rchitecture,简称UMA)。 而在多CPU体系结构的计算机中,物理存储空间的属性却往往不尽相同。每个CPU都可能有自 己的本地存储器,系统中还可以有公用的共享存储器,这些存储器一起构成一个物理地址连 续的系统存储空间;每个CPU访问其本地存储器是最快的,而访问其他CPU节点的存储器或共 享存储器则较慢,因此这就使得不同物理地址处的存储器的属性可能不一样。这种存储结构 也就称为"非均质存储结构"(Non-Uniform Memory Architecture,简称NUMA)。在NUMA
结构的系统中,属于同一个CPU节点的存储器模块通常是匀质的,因此也被称为"存储节点" (Memory Node)。
Linux内核2.4.0版开始增加对NUMA的支持,内存区就不再是全局的最高层结
构,而是从属于某一个具体的存储节点。
ucLinux在头文件include/linux/mmzone.h中定义了数 据结构pglist_data来描述一个存储节点:
typedef struct pglist_data {
zone_t node_zones[MAX_NR_ZONES]; //3种分配区
zonelist_t node_zonelists[NR_GFPINDEX]; //256种分配类型
struct page *node_mem_map; // 分配域的页结构表
unsigned long *valid_addr_bitmap;
struct bootmem_data *bdata; //内核自举时所使用的分配域,大家在初始化的时候要极度注意该区域,在初始化的时候,是又改结构负责管理内存的使用d
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; //分配域结构
1. 若干存储节点的pglist_data数据结构通过指针node_next形成一个单向链接的存储节点队 列。不过一般的嵌入式的环境我估计也就一个这样的结构啦。
2. 系统仍然维护一个全局的page结构数组mem_map,但是每个存储节点都通过pglist_data结 构中的指针node_mem_map指向mem_map数组中属于该存储节点的起始物理页帧的page结构。
3. 数组node_zones[MAX_NR_ZONES]定义了属于该存储节点的内存区。
4. 数组node_zonelists[NR_GFPINDEX]定义了拥有该存储节点的CPU在分配连续的物理页帧块 时可以选择使用物理内存区。
显然,我们的板子暂时不支持努NUMA,但是内核的数据结构确实按照NUMA的方式构建的
在文件---linux/mmnommu/numa.c中定义了存储节点,显然,我们只有一个这样的结构。
static bootmem_data_t contig_bootmem_data;
pg_data_t contig_page_data = { bdata: &contig_bootmem_data };它是一个代表本节点的内存信息的一个数据结构
数据结构zonelist_struct的定义如下:
typedef struct zonelist_struct{
zone_t *zones[MAX_NR_ZONES+1];
unsigned long gfp_mask;
}zonelist_t;
显然,一个zonelist_struct结构为存储节点定义了一种可供选择的分配策略。也即当从一个 存储节点分配连续的物理页帧块时,可以从zones指针数组中寻找可满足此次分配需求的物理 内存区。
ucLinux在头文件include/linux/mmzone.h中定义了数 据结构zone_struct
typedef struct zone_struct {
spinlock_t lock;
unsigned long free_pages; //空闲页面的数量
unsigned long pages_min, pages_low, pages_high; //判断页面使用情况的阈值
int need_balance;
free_area_t free_area[MAX_ORDER]; //描述空闲页面
注意: uclinux通过zone_struct数据结构中一组"空闲区"(free area)队列来管理包含在其中的物理页帧,也即free_area[MAX_ORDER]数组。为什么是通过一组"空闲区"(free area)队列,而不是一个"空闲区"(free area)队列呢?这是因为我们常常需要成"块"地分配物理地址连续的多个物理页帧。因此,在zone_struct数据结构中即要有一个队列来保持一些离散(连续长度为1)的物理页帧,还要有其他多个队列来保持连续长度为2、4、8、16、…、2MAX_ORDER的页帧块。常数MAX_ORDER值为10,因此在Linux中最大的连续页帧块可以达到210=1024个页帧,即4M字节大小。
wait_queue_head_t * wait_table;
unsigned long wait_table_size;
unsigned long wait_table_shift;
struct pglist_data *zone_pgdat; //指向该内存区(zone)属于的节点信息。
struct page *zone_mem_map; //指向本zone的第一个page结构
unsigned long zone_start_paddr; //本zone开始的物理地址
unsigned long zone_start_mapnr; //用来表示该内存区所包含的物理页帧在page数组中的起始页帧序号
char *name;
unsigned long size;
} zone_t;
该数据结构描述了物理内存区的信息。为了更加方便地管理和使用物理地址空间,Linux将整个连续的物理地址空间划分为三段,每一段物理地址空间形成一个物理内存区(zone),分别是:ZONE_DMA区、ZONE_NORMAL区和ZONE_HIGHMEM区。其中,ZONE_DMA区用于管理低端的16M物理内存范围,这一段物理内存是转供DMA使用的。ZONE_HIGHMEM区用于管理高端的1GB以上的物理内存(如果有的话)。 每一个物理页帧根据其页序号(也即它的物理地址范围)决定它隶属于哪一个物理内存区,而且这种隶属关系是永久不变的。
在我们的板子上,共有三个zone,定义如下:
#define ZONE_DMA 0
#define ZONE_NORMAL 1
#define ZONE_HIGHMEM 2
#define MAX_NR_ZONES 3
但是实际上,所有的内存页都保存在了ZONE_NORMAL中
前面罗嗦了这么久,其实好大部分都是一些内核前辈的观点,这里罗列出来,以便使下面的分析会顺畅点
第二节 bootmem_init 和 paging_init
系统初始化时对内存的初始化源自下列代码(删掉了若干不相关代码)
void __init setup_arch(char **cmdline_p)
{
1 if (meminfo.nr_banks == 0) {
2 meminfo.nr_banks = 1;
3 meminfo.bank[0].start = PAGE_OFFSET;
4 meminfo.bank[0].size = MEM_SIZE;
5 }
6 bootmem_init(&meminfo); //初始化bootmem
7 paging_init(&meminfo, mdesc); // mem_map is set up here!
}
解释:
1------5行
在这里,我们设定meminfo结构,用此结构描述我们的内存基本情况,之后,会用该变量初始化contig_page_data的数据
#define PHYS_OFFSET (DRAM_BASE)
#define PAGE_OFFSET PHYS_OFFSET
具体的内存设置的值来自config.h文件,具体的板子具体分析
正常的内存管理应该由pg_data_t、 zone、 mem_map 等数据结构来管理,但此时这些结构还没有建立,bootmem是在初始化的时候用来描述内存使用情况。
void __init bootmem_init(struct meminfo *mi)
{
struct node_info node_info[NR_NODES], *np = node_info;
unsigned int bootmap_pages, bootmap_pfn, map_pg;
int node, initrd_node;
bootmap_pages = find_memend_and_nodes(mi, np);后面会详细分析这个函数,为了在启动阶段描述内存使用情况我们需要一些内存空间,这些空间叫做bootmem,此时bootmap_pages表明了bootmem所需要的pages的数目
bootmap_pfn = find_bootmap_pfn(0, mi, bootmap_pages); 后面会详细分析这个函数,通过这个函数,bootmap_pfn设置了bootmem所在的初始页号。也就是说从bootmap_pfn到bootmap_pfn + bootmap_pages的内存页被用来描述初始化的时候的内存的用用情况
initrd_node = check_initrd(mi);//俺们的板子没有用
map_pg = bootmap_pfn;
np += numnodes - 1;
初始化node结构
for (node = numnodes - 1; node >= 0; node--, np--) {
if (np->end == 0) {
if (node == 0)
BUG();
continue;
}
init_bootmem_node(NODE_DATA(node), map_pg, np->start, np->end); 后面会详细分析这个函数
free_bootmem_node_bank(node, mi);//释放所有内存,也就是把bootmem的区域全部设置为0
map_pg += np->bootmap_pages;
我们有可能会保留一些内存以便使值不能被动态分配,具体要保留什么内容,后面会详细分析
if (node == 0)
reserve_node_zero(bootmap_pfn, bootmap_pages);
}
if (map_pg != bootmap_pfn + bootmap_pages)
BUG();
}
void __init paging_init(struct meminfo *mi, struct machine_desc *mdesc)
{
void *zero_page;
int node;
memcpy(&meminfo, mi, sizeof(meminfo));
分配一个页面的内存,很显然,我们刚才做的工作起了作用,我们会在表示该页面的那个bootmem中的bit设置值为1,以此表示该页面已经被分配出去拉
zero_page = alloc_bootmem_low_pages(PAGE_SIZE);
unsigned long zone_size[MAX_NR_ZONES] = {0,0,0};
zone_size[ZONE_DMA] = 0;
zone_size[ZONE_NORMAL] = (END_MEM - PAGE_OFFSET) >> PAGE_SHIFT;
free_area_init_node(0, NULL, NULL, zone_size, PAGE_OFFSET, NULL);后面会详细分析这个函数
memzero(zero_page, PAGE_SIZE);
empty_zero_page = virt_to_page(zero_page);
flush_dcache_page(empty_zero_page);//抱歉,我也搞不太懂,应该是和cpu体系相关的
}
void __init free_area_init_node(int nid, pg_data_t *pgdat, struct page *pmap,
unsigned long *zones_size, unsigned long zone_start_paddr,
unsigned long *zholes_size)
{
free_area_init_core(0, &contig_page_data, &mem_map, zones_size,
zone_start_paddr, zholes_size, pmap);
}
/*
* Set up the zone data structures:
* - mark all pages reserved
* - mark all memory queues empty
* - clear the memory bitmaps
*/
void __init free_area_init_core(int nid, pg_data_t *pgdat, struct page **gmap,
unsigned long *zones_size, unsigned long zone_start_paddr,
unsigned long *zholes_size, struct page *lmem_map)
{
unsigned long i, j;
unsigned long map_size;
unsigned long totalpages, offset, realtotalpages;
const unsigned long zone_required_alignment = 1UL << (MAX_ORDER-1);
if (zone_start_paddr & ~PAGE_MASK)
BUG();
totalpages = 0;
for (i = 0; i < MAX_NR_ZONES; i++) {
unsigned long size = zones_size ;
totalpages += size;
}
realtotalpages = totalpages;
if (zholes_size)
for (i = 0; i < MAX_NR_ZONES; i++)
realtotalpages -= zholes_size;
printk("On node %d totalpages: %lu/n", nid, realtotalpages);
/*
* Some architectures (with lots of mem and discontinous memory
* maps) have to search for a good mem_map area:
* For discontigmem, the conceptual mem map array starts from
* PAGE_OFFSET, we need to align the actual array onto a mem map
* boundary, so that MAP_NR works.
*/
给表示所有物理内存的page结构数组分配空间
map_size = (totalpages + 1)*sizeof(struct page);
if (lmem_map == (struct page *)0) {
lmem_map = (struct page *) alloc_bootmem_node(pgdat, map_size);
lmem_map = (struct page *)(PAGE_OFFSET +
MAP_ALIGN((unsigned long)lmem_map - PAGE_OFFSET));
}
*gmap = pgdat->node_mem_map = lmem_map;
pgdat->node_size = totalpages;
pgdat->node_start_paddr = zone_start_paddr;
pgdat->node_start_mapnr = (lmem_map - mem_map);
pgdat->nr_zones = 0;
offset = lmem_map - mem_map;
下面给zone_struct赋值
for (j = 0; j < MAX_NR_ZONES; j++) {
zone_t *zone = pgdat->node_zones + j;
unsigned long mask;
unsigned long size, realsize;
zone_table[nid * MAX_NR_ZONES + j] = zone;
realsize = size = zones_size[j];
if (zholes_size)
realsize -= zholes_size[j];
printk("zone(%lu): %lu pages./n", j, size);
zone->size = size;
zone->name = zone_names[j];
zone->lock = SPIN_LOCK_UNLOCKED;
zone->zone_pgdat = pgdat;
zone->free_pages = 0;
zone->need_balance = 0;
if (!size)
continue;
/*
* The per-page waitqueue mechanism uses hashed waitqueues
* per zone.
*/
zone->wait_table_size = wait_table_size(size);
zone->wait_table_shift =
BITS_PER_LONG - wait_table_bits(zone->wait_table_size);
zone->wait_table = (wait_queue_head_t *)
alloc_bootmem_node(pgdat, zone->wait_table_size
* sizeof(wait_queue_head_t));
for(i = 0; i < zone->wait_table_size; ++i)
init_waitqueue_head(zone->wait_table + i);
pgdat->nr_zones = j+1;
mask = (realsize / zone_balance_ratio[j]);
if (mask < zone_balance_min[j])
mask = zone_balance_min[j];
else if (mask > zone_balance_max[j])
mask = zone_balance_max[j];
zone->pages_min = mask;
zone->pages_low = mask*2;
zone->pages_high = mask*3;
zone->zone_mem_map = mem_map + offset;
zone->zone_start_mapnr = offset;
zone->zone_start_paddr = zone_start_paddr;
if ((zone_start_paddr >> PAGE_SHIFT) & (zone_required_alignment-1))
printk("BUG: wrong zone alignment, it will crash/n");
/*
* Initially all pages are reserved - free ones are freed
* up by free_all_bootmem() once the early boot process is
* done. Non-atomic initialization, single-pass.
*/
处理该zone_struct上的所有页面,具体含义由函数名可猜出
for (i = 0; i < size; i++) {
struct page *page = mem_map + offset + i;
set_page_zone(page, nid * MAX_NR_ZONES + j);
set_page_count(page, 0);
SetPageReserved(page);
INIT_LIST_HEAD(&page->list);
if (j != ZONE_HIGHMEM)
set_page_address(page, __va(zone_start_paddr));
zone_start_paddr += PAGE_SIZE;
}
//初始化zone->free_area
offset += size;
//为zone的buddy bitmap分配内存
for (i = 0; ; i++) {
unsigned long bitmap_size;
INIT_LIST_HEAD(&zone->free_area.free_list);
if (i == MAX_ORDER-1) {
zone->free_area.map = NULL;
break;
}
//求出buddy bitmap的字节数
bitmap_size = (size-1) >> (i+4);
bitmap_size = LONG_ALIGN(bitmap_size+1);
zone->free_area.map =
(unsigned long *) alloc_bootmem_node(pgdat, bitmap_size);
}
}
//构建页面分配的策略,也就是填充zonelists结构
build_zonelists(pgdat);
}