uclinux的内存初始化---------3

 概要:本文主要讲解了内存初始化所涉及到的三个函数
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);
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值