uclinux2.6(bf561)内核中的paging_init

来源:互联网 发布:linux开放端口命令 编辑:程序博客网 时间:2024/05/18 13:48
 
   
1   参数及变量
在bootmem初始化完成之后,setup_arch开始进行下一个初始化工作:
     /*
      * get kmalloc into gear
      */
     paging_init();
这个函数的实现位于arch/mm/init.c:
 
/*
 * paging_init() continues the virtual memory environment setup which
 * was begun by the code in arch/head.S.
 * The parameters are pointers to where to stick the starting and ending
 * addresses of available kernel virtual memory.
 */
void __init paging_init(void)
{
     /*
      * make sure start_mem is page aligned, otherwise bootmem and
      * page_alloc get different views og the world
      */
     unsigned long end_mem = memory_end & PAGE_MASK;
 
     pr_debug("start_mem is %#lx   virtual_end is %#lx/n", PAGE_ALIGN(memory_start), end_mem);
 
     /*
      * initialize the bad page table and bad page to point
      * to a couple of allocated pages
      */
     empty_bad_page_table = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
     empty_bad_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
     empty_zero_page = (unsigned long)alloc_bootmem_pages(PAGE_SIZE);
     memset((void *)empty_zero_page, 0, PAGE_SIZE);
 
     /*
      * Set up SFC/DFC registers (user data space)
      */
     set_fs(KERNEL_DS);
 
     pr_debug("free_area_init -> start_mem is %#lx   virtual_end is %#lx/n",
             PAGE_ALIGN(memory_start), end_mem);
 
     {
         unsigned long zones_size[MAX_NR_ZONES] = { 0, };
 
         zones_size[ZONE_DMA] = (end_mem - PAGE_OFFSET) >> PAGE_SHIFT;
         zones_size[ZONE_NORMAL] = 0;
#ifdef CONFIG_HIGHMEM
         zones_size[ZONE_HIGHMEM] = 0;
#endif
         free_area_init(zones_size);
     }
}
这个函数看起来相当简单,当内核执行到这里的时候,memory_end指向SDRAM的最后一个页的首字节,对于64M的SDRAM而言,其值为0x3fff000。alloc_bootmem_pages函数将以页(4096字节)为单位分配指定大小的内存。
这里比较有意思的是最后一个调用free_area_init。
2   free_area_init
这个函数的实现在mm/page_alloc.c中:
void __init free_area_init(unsigned long *zones_size)
{
     free_area_init_node(0, NODE_DATA(0), zones_size,
              __pa(PAGE_OFFSET) >> PAGE_SHIFT, NULL);
}
其中的__init标记指明了这个函数将只调用一次,当内核执行到此的时候,其参数zone_size将有2个元素,其值分别为0x3fff和0,代表了ZONE_DMA和ZONE_NORMAL这两个区间的页面数量。
在这个函数中,NODE_DATA定义为:
extern struct pglist_data contig_page_data;
#define NODE_DATA(nid)      (&contig_page_data)
PAGE_OFFSET定义为0。
__pa定义为:
#define __pa(vaddr)         virt_to_phys((void *)(vaddr))
#define virt_to_phys(vaddr) ((unsigned long) (vaddr))
3   free_area_init_node
这个函数的实现在mm/page_alloc.c中:
 
void __meminit free_area_init_node(int nid, struct pglist_data *pgdat,
         unsigned long *zones_size, unsigned long node_start_pfn,
         unsigned long *zholes_size)
{
     pgdat->node_id = nid;
     pgdat->node_start_pfn = node_start_pfn;
     calculate_node_totalpages(pgdat, zones_size, zholes_size);
 
     alloc_node_mem_map(pgdat);
 
     free_area_init_core(pgdat, zones_size, zholes_size);
}
内核执行到这里时,参数nid为0;pgdat指向一个固定的全局变量contig_page_data,且此变量的struct bootmem_data *bdata已经初始化完成;zone_size是一个有两个元素的数组,其值为{0x3fff, 0},分别代表了ZONE_DMA和ZONE_NORMAL两个区域的页表数量;node_start_pfn为0;zholes_size为NULL。
4   calculate_node_totalpages
此函数的实现在mm/page_alloc.c中:
static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,
         unsigned long *zones_size, unsigned long *zholes_size)
{
     unsigned long realtotalpages, totalpages = 0;
     enum zone_type i;
 
     for (i = 0; i < MAX_NR_ZONES; i++)
         totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,
                                     zones_size);
     pgdat->node_spanned_pages = totalpages;
 
     realtotalpages = totalpages;
     for (i = 0; i < MAX_NR_ZONES; i++)
         realtotalpages -=
              zone_absent_pages_in_node(pgdat->node_id, i,
                                     zholes_size);
     pgdat->node_present_pages = realtotalpages;
     printk(KERN_DEBUG "On node %d totalpages: %lu/n", pgdat->node_id,
                                 realtotalpages);
}
很简单,就是设置pgdat->node_spanned_pagespgdat->node_present_pages两个成员的值,在这里调用的zone_spanned_pages_in_nodezone_absent_pages_in_node两个函数都非常简单:
static inline unsigned long zone_spanned_pages_in_node(int nid,
                       unsigned long zone_type,
                       unsigned long *zones_size)
{
     return zones_size[zone_type];
}
 
static inline unsigned long zone_absent_pages_in_node(int nid,
                            unsigned long zone_type,
                            unsigned long *zholes_size)
{
     if (!zholes_size)
         return 0;
 
     return zholes_size[zone_type];
}
因此最终pgdat->node_spanned_pagespgdat->node_present_pages两个成员的值都将为SDRAM的页表数量,对于64M内存而言,其值为0x3fff。
 
5   alloc_node_mem_map
此函数的实现为:
 
static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)
{
     /* Skip empty nodes */
     if (!pgdat->node_spanned_pages)
         return;
 
#ifdef CONFIG_FLAT_NODE_MEM_MAP
     /* ia64 gets its own node_mem_map, before this, without bootmem */
     if (!pgdat->node_mem_map) {
         unsigned long size, start, end;
         struct page *map;
 
         /*
          * The zone's endpoints aren't required to be MAX_ORDER
          * aligned but the node_mem_map endpoints must be in order
          * for the buddy allocator to function correctly.
          */
         start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);
         end = pgdat->node_start_pfn + pgdat->node_spanned_pages;
         end = ALIGN(end, MAX_ORDER_NR_PAGES);
         size = (end - start) * sizeof(struct page);
         map = alloc_remap(pgdat->node_id, size);
         if (!map)
              map = alloc_bootmem_node(pgdat, size);
         pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);
     }
#ifndef CONFIG_NEED_MULTIPLE_NODES
     /*
      * With no DISCONTIG, the global mem_map is just set as node 0's
      */
     if (pgdat == NODE_DATA(0)) {
         mem_map = NODE_DATA(0)->node_mem_map;
#ifdef CONFIG_ARCH_POPULATES_NODE_MAP
         if (page_to_pfn(mem_map) != pgdat->node_start_pfn)
              mem_map -= pgdat->node_start_pfn;
#endif /* CONFIG_ARCH_POPULATES_NODE_MAP */
     }
#endif
#endif /* CONFIG_FLAT_NODE_MEM_MAP */
}
当内核执行到此函数时,pgdat指向全局变量
struct pglist_data contig_page_data = { .bdata = &contig_bootmem_data };
可以认为在内核中所有的pglist_data的指针都指向这个全局变量。
pgdat->node_spanned_pages的值为SDRAM中的页表数量,对于64M而言,其值为0x3fff (16K)。
pgdat->node_start_pfn为SDRAM的起始位置,为0。
alloc_remap函数直接返回一个空指针。
alloc_bootmem_node用于以页为单位分配指定的空间。
MAX_ORDER_NR_PAGES的定义为:
/* Free memory management - zoned buddy allocator. */
#ifndef CONFIG_FORCE_MAX_ZONEORDER
#define MAX_ORDER 11
#else
#define MAX_ORDER CONFIG_FORCE_MAX_ZONEORDER
#endif
#define MAX_ORDER_NR_PAGES (1 << (MAX_ORDER - 1))
因此这个函数的功能就很简单了,就是为pgdat->node_mem_map分配内存空间。
6   free_area_init_core
这个函数也在mm/page_alloc.c中,其声明如下:
 
/*
 * Set up the zone data structures:
 *   - mark all pages reserved
 *   - mark all memory queues empty
 *   - clear the memory bitmaps
 */
static void __meminit free_area_init_core(struct pglist_data *pgdat,
         unsigned long *zones_size, unsigned long *zholes_size)
{
此函数主要初始化了pgdat->node_zones,代码较长,有空再写。
原创粉丝点击