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; }

 

posted @ 2014-05-14 11:32  liuzhijiang123  阅读(754)  评论(0编辑  收藏  举报