话说在linux内核初始化完内核临时页表来映射前8MB内存后,在欲望的驱使下,linux开始着手抢占896MB以下的内存了。然而抢夺这896MB内存绝非易事。linux还有许多麻烦的事要做。比如说进攻和占领伊拉克等等。于是setup_memory函数开始为linux策划和筹备此事。主要任务是建立一张与物理内存页框号对应的位图。如果该页框被占用则对应位图中的位被置1,否则置0。在伙伴系统建立前对内存进行临时管理。在初始化内核最终页表时alloc_bootmem_low_pages内存分配函数就用到了这个位图。
核心数据结构:
typedef struct bootmem_data { unsigned long node_boot_start; /* 起始页框号0 */ unsigned long node_low_pfn; /* 如果物理内存大于896MB,node_low_pfn为0x37FFF。 如果物理内存小于等于896MB,node_low_pfn为物理内存的最大页框号。*/ void *node_bootmem_map; /* 位图的起始地址 */ /* 下面三个变量的作用用于alloc_bootmem_low_pages函数 */ unsigned long last_offset; unsigned long last_pos; unsigned long last_success; } bootmem_data_t;创建位图的函数流程:
start_kernel->setup_arch->setup_memory->init_bootmem->init_bootmem_core 。
linux/arch/i386/kernel/setup.c
在setup_memory()中执行
start_pfn = PFN_UP(init_pg_tables_end);
/* start_pfn是指映射8MB所用页表之后第一个页框号。PFN_UP的作用是把地址按页的大小进行对齐。也就是地址是4kb的整数倍大小。*/
find_max_pfn();
/* 找到物理内存中最大页框号。放入max_pfn中。*/
max_low_pfn=find_max_low_pfn();
/* 如果物理地址小于等于896MB,max_low_pfn=max_pfn。大于896MB时,max_low_pfn为896MB内存大小的最大页框号 */
bootmap_size = init_bootmem(start_pfn,max_low_pfn);
进入init_bootmem中
unsigned long __init init_bootmem (unsigned long start, unsigned long pages) { max_low_pfn = pages; min_low_pfn = start; return( init_bootmem_core( NODE_DATA(0), start, 0, pages)); }再从init_bootmem进入init_bootmem_core中,此函数创建位图
static unsigned long __init init_bootmem_core ( pg_data_t *pgdat, unsigned long mapstart, unsigned long start, unsigned long end) { bootmem_data_t *bdata = pgdat->bdata; unsigned long mapsize = (( end - start)+7)/8;/* mapsize为要创建的位图的大小 */ pgdat->pgdat_next = pgdat_list; pgdat_list = pgdat; mapsize = (mapsize + (sizeof(long) - 1UL)) & ~(sizeof(long) - 1UL);/* 按照4个字节大小进行对齐 */ bdata->node_bootmem_map = phys_to_virt(mapstart << PAGE_SHIFT);/* bdata->node_bootmem_map 为位图的起始位置的线性地址 */ bdata->node_boot_start = ( start << PAGE_SHIFT);/* 第一个页框的起始地址,就是0号页框的起始地址0x00000000 */ bdata->node_low_pfn = end; memset(bdata->node_bootmem_map, 0xff, mapsize); /* 把位图中的每一位都置1,表示为占用状态。在接下去的函数中,会把内核可以使用的页框号对应的位置0。 */ return mapsize;}回到setup_memory中,接着执行
register_bootmem_low_pages(max_low_pfn);
此函数的主要的功能是把内核可以使用的页框号(最大为max_low_pfn)在位图中对应的位置0。
由于有些页已经被内核数据占用,所以还要把这些页再置为1。就是调用
reserve_bootmem( HIGH_MEMORY, ( PFN_PHYS(start_pfn) + bootmap_size + PAGE_SIZE-1) - ( HIGH_MEMORY));此函数把从1MB开始(HIGH_MEMORY定义为1024*1024)到位图结束所占用的页框号在位图中对应的位置1。
然后根据不同的硬件配置再做一些扫尾的工作。
return max_low_pfn;
setup_memory函数结束。
接着linux就会调用paging_init来完成它对896MB内存的抢夺了。