Linux内存管理 (二)初始化内存管理
对内存管理的数据结构初始化由start_kernel开始,它的流程如下所示
start_kernel( ) { setup_arch( );//负责特定于体系结构的相关数据的设置 setup_per_cup_areas();// build_all_zonelists( );//建立结构和内存域的数据结构 mem_init( );//停用bootmem分配器并迁移到实际的内存管理函数 setup_per_cup_pageset( );// }
1 setup_arch 流程如下:
setup_arch( ) { machine_specific_memory_setup( );//创建一个列表,包括系统占据的内存区和空间内存区.显示内存区的相关信息 parse_early_param( );//分析命令行,允许管理员手工配置内存区的分配情况 setup_memory( );//启动内存分配,自举分配器 paging_init( );//初始化页表并启用内存分页 zone_sizes_init( );//负责初始化系统中所有结点pg_data_t实例. }
(1)setup_memory任务
由于setup_memory重要的一项任务初始化自举分配器,因此以下就是相应的自举分配器所使用的数据结构
typedef struct bootmem_data { unsigned long node_boot_start; //系统第一个物理页的编号,一般情况下为0 unsigned long node_low_pfn; //自举分配器所能直接管理的最后一个页的编号,即ZONE_NORMAL的结束页 void *node_bootmem_map; //指向内存分配位图的内存区指针 unsigned long last_offset; //如果分配请求没有请求分配整个页,则last_offset用作该页内部的偏移量 unsigned long last_pos; //上一次分配的页的编号. unsigned long last_success; //位图中上一次成功分配内存的位置 struct list_head list; //链表,内存可以有多个自举分配器 } bootmem_data_t;
setup_memory的流程如下:
setup_memory( )
{
进行相应的计算,确定可用的低端内存页帧//全局变量max_low_pfn保存可映射的最高页的编号
stup_boot_alloctor( )
{
init_bootmem( );//检测低端内存,把信息保存到相应的bootmem_data_t实例中,默认为contig_bootmem_data
register_bootmem_low_pages( );//将位图中对应的比特为清零,释放所有潜在可用的内存页
reserve_bootmem(bootmap, bootmap_size);//分配内存用于自举分配器
}
}
在初始化期,内核提供了相应的接口用于从自举分配器所管理的内存中分配内存
alloc_bootmem(size), alloc_bootmem_pages(size)是两个前端,alloc_bootmem_low(size), alloc_bootmem_low_pages(size)也是两个前端,核心的分配是__alloc_bootmem()等
(2)paging_init流程
paging_init( )
{
pagetable_init( ) //初始化系统的页表,以swapper_pg_dir为基础
{
kernel_physical_mapping_init( );//将物理内存页(0~896M)映射到虚拟地址空间中(由PAGE_OFFSET开始)
初始化固定映射
permanent_kmaps_init( );
}
load_cr3( );//将cr3寄存器设置为指向全局页目录(swapper_pg_dir)
__flush_all_tlb( );
kmap_init( );
}
2 build_all_zonelists 结点备用内存域列表的初始化
zonelist的结构如下:
<mmzone.h>
struct zonelist { struct zone *zones[MAX_ZONES_PER_ZONELIST + 1]; /*以NULL结尾,定义某一个内存域的备用内存域(包含自身),代价由低到高*/ };
pg_data_t.node_zonelists的初始化代码如下:
<mm/page_alloc.c>
static void build_zonelists(pg_data_t *pgdat)/*pgdat表示内存结点*/ { int node, local_node; enum zone_type i,j; /*内存域的枚举变量*/ local_node = pgdat->node_id;/*内存结点id*/
/*for循环依次迭代本内存结点的各个内存域的备用内存域列表*/ for (i = 0; i < MAX_NR_ZONES; i++) { struct zonelist *zonelist; zonelist = pgdat->node_zonelists + i; j = build_zonelists_node(pgdat, zonelist, 0, i); /*将本内存结点中比内存域i代价更大或相等的内存域作为i的备用*/
/*计算内存节点比local_node更大的内存结点*/ for (node = local_node + 1; node < MAX_NUMNODES; node++) { if (!node_online(node)) continue; j = build_zonelists_node(NODE_DATA(node), zonelist, j, i); }
/*从头开始计算*/ for (node = 0; node < local_node; node++) { if (!node_online(node)) continue; j = build_zonelists_node(NODE_DATA(node), zonelist, j, i); } zonelist->zones[j] = NULL; } }
<mm/page_alloc.c>
/*
* pgdat表示备用内存结点的实例, zonelist表示某个内存结点中指定内存域zone_type的备用内存域列表, nr_zones指定已经分配好的备用内存域数目, zone_type指定内存域
* 该函数的作用就是将pgdat中的内存域作为备用内存域保存在zonelist中
*/
static int build_zonelists_node(pg_data_t *pgdat, struct zonelist *zonelist,int nr_zones, enum zone_type zone_type) { struct zone *zone; zone_type++; do { zone_type--;/*寻求更加昂贵的内存域作为指定内存域的备用结点*/ zone = pgdat->node_zones + zone_type; if (populated_zone(zone)) { zonelist->zones[nr_zones++] = zone; } } while (zone_type); return nr_zones; }