再读uclinux-2008r1(bf561)内核存储区域管理(2):可用页表初始化

 

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

 

 

本文适用于

ADI bf561 DSP

优视BF561EVB开发板

uclinux-2008r1-rc8 (移植到vdsp5)

Visual DSP++ 5.0

   

欢迎转载,但请保留作者信息

 

1.1.1   可用页表初始化

在使用可用内存之前,内核必须首先计算有多少空闲页可用,并为每页都分配一个page结构体进行描述。这就是页表初始化做的工作。

1.1.1.1             paging_init

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(实际限制为 60M )而言,其值为0x3bff000alloc_bootmem_pages函数将以页(4096字节)为单位分配指定大小的内存。

这里比较有意思的是最后一个调用free_area_init

1.1.1.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个元素,其值分别为0x3bff0,代表了ZONE_DMAZONE_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))

1.1.1.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);

}

内核执行到这里时,参数nid0pgdat指向一个固定的全局变量contig_page_data,且此变量的struct bootmem_data *bdata已经初始化完成zone_size是一个有两个元素的数组,其值为{0x3bff, 0},分别代表了ZONE_DMAZONE_NORMAL两个区域的页表数量;node_start_pfn0zholes_sizeNULL

calculate_node_totalpages函数将设置pgdatnode_spanned_pagesnode_present_pages两个成员的值。最终pgdat->node_spanned_pagespgdat->node_present_pages两个成员的值都将为SDRAM的页表数量,对于 64M 内存而言(实际限制为 60M ),其值为0x3bff

alloc_node_mem_map这个函数将使用bootmempgdat->node_mem_map分配内存空间,记住,node_mem_map这个成员将指向page结构体数组的第一个元素。

1.1.1.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 内存而言(实际限制为 60M ),其值为0x3bff

1.1.1.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 而言(实际限制为 60M ),其值为0x3bff (16K)

pgdat->node_start_pfnSDRAM的起始位置,为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分配内存空间,记住,node_mem_map这个成员将指向page结构体数组的第一个元素。

 

 

参考资料

uClinux2.6(bf561)中的CPLB( 2008/2/19 )

uclinux2.6(bf561)中的bootmem分析(1):猜测( 2008/5/9 )

uclinux2.6(bf561)中的bootmem分析(2):调用前的参数分析( 2008/5/9 )

uclinux2.6(bf561)中的bootmem分析(3)init_bootmem_node( 2008/5/9 )

uclinux2.6(bf561)中的bootmem分析(4)alloc_bootmem_pages( 2008/5/9 )

uclinux2.6(bf561)内核中的paging_init( 2008/5/12 )

uclinux-2008r1(bf561)内核的icache支持(1):寄存器配置初始化( 2008/5/16 )

uclinux-2008r1(bf561)内核的icache支持(2)icplb_table的生成( 2008/5/16 )

uclinux-2008r1(bf561)内核的icache支持(3)__fill_code_cplbtab( 2008/5/16 )

uclinux-2008r1(bf561)内核的icache支持(4):换页问题( 2008/5/16 )

再读uclinux-2008r1(bf561)内核中的bootmem( 2008/6/3 )

uclinux-2008r1(bf561)内核中与存储管理相关的几个全局变量( 2008/6/4 )

uclinux-2008r1(bf561)内核存储区域初探( 2008/6/4 )

uclinux-2008r1(bf561)内核中的zonelist初始化( 2008/6/5 )

uclinux-2008r1(bf561)内核中内存管理相关的几个结构体( 2008/6/5 )

再读内核存储管理(1):相关的全局变量( 2008/6/17 )

再读内核存储管理(2):相关的数据结构( 2008/6/17 )

再读内核存储管理(3)bootmem分配策略( 2008/6/17 )

再读内核存储管理(4):存储区域管理( 2008/6/17 )

再读内核存储管理(5)buddy算法( 2008/6/17 )

再读内核存储管理(6):高速缓存的应用( 2008/6/17 )

再读内核存储管理(7)icache支持( 2008/6/17 )

再读内核存储管理(8):片内SRAM的使用( 2008/6/17 )

初读SLAB( 2008/6/26 )

三读bootmem( 2008/7/24 )

再读uclinux-2008r1(bf561)内核存储区域管理(1):相关数据结构( 2008/7/25 )

 

 

 

 

 

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌云阁主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值