DSP

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

2019-07-13 16:56发布

  快乐虾 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的最后一个页的首字节,对于64MSDRAM(实际限制为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)