Linux的内存初始化

Linux的内存初始化

一.FS2410硬件平台

Fs2410开发板基于三星的S3C2410芯片,主频203MHZS3C2410的存储空间由8个组(bank)组成,每个组的大小是128M,所以128MX8=1G。其中bank0bank5用于ROM或者SRAMbank6bank7可用于ROM, SRAM, SDRAMFs2410开发板的外部存储器有内存:64M字节,NOR flash2M字节,NAND flash64M字节。其中根据跳线的不同,系统启动时可能选择不同的启动方式,如从NOR启动,还是NAND启动,不同的启动方式不在本文的讨论范围之内,可以参考另一篇文章:http://blog.csdn.net/lieye_leaves/article/details/8849777

其中64M内存的起始地址是从bank6开始的,地址是0x3000 0000, 结束地址0x3400 0000

二.Linux代码选择

选择分析的linux的版本是linux-2.6.26.5FS2410的体系结构代码选择的是linux/arch/arm/mach-s3c2410下的该体系下的代码,该体系下系统启动时,linux内核被加载到地址0x3000 8000

三.Linux内核的内存分布

在编译内核时会产生文件system.map,在该文件中会保存内核的各个函数的地址,在该文件中有如下的变量(我选择了我编译的某个版本的system.map文件)

c0008000 T stext

    该部分为内核代码

c01b3000 A _etext

    该部分为内核数据

c01c8408 D _edata

    该部分为用于初始化的数据

c01e5208 B _end

以上部分的地址为内核的虚拟地址,减去0xc000 0000,即为物理地址。

 

四 linux内存的初始化

    linux的启动过程中和启动完成后采用的内存分配的方式是不一样的,在启动过程中使用位图分配方式,在启动完成后采用伙伴系统。在start_kernel()setup_arch()中先将系统初始化成位图的方式,此后的内存分配采用位图分配方式,其中伙伴系统的页描述符数组mem_map[]就是采用在此处采用位图分配的方式分配的,函数执行,直到mem_init()将不需要的位图分配中的内存释放到伙伴系统,系统内存逐步转化成伙伴系统。

Linux位图分配方式的初始化过程,代码的大体位置如下:

Start_kernel/setup_arch/paging_init/bootmem_init/bootmem_init_node

FS2410的内存配置只有一个bank,数据如下:

 struct membank {

       unsigned long start;     // 0x3000 0000

       unsigned long size;      // 0x0400 0000

       int           node;

};

内存采用页框的方式,每个页框的大小为4k,内存从0x3000 0000----0x3400 0000

4.1位图内存分配初始化

4.1.1 位图总数

在位图内存分配时,每个页框用一个位来表示,

start = bank->start >> PAGE_SHIFT;  //PAGE_SHIFT = 12,即为4k的大小

end = (bank->start + bank->size) >> PAGE_SHIFT;

 

if (start_pfn > start)

                     start_pfn = start;

              if (end_pfn < end)

                     end_pfn = end;

 

boot_pages = bootmem_bootmap_pages(end_pfn - start_pfn);

函数详细如下:

pages = end_pfn – start_pfn

mapsize = (pages+7)/8;

mapsize = (mapsize + ~PAGE_MASK) & PAGE_MASK; 

//#define PAGE_SIZE  (1UL << PAGE_SHIFT)

//#define PAGE_MASK            (~(PAGE_SIZE-1))

  mapsize >>= PAGE_SHIFT;

// mapsize= 0x1000

// mapsize= 0x1

       return mapsize;

//boot_pages = mapsize

由以上的计算可知,使用一个页框的位图可以表示所有的页框的分配状态

//////////////////////////////////////

4.1.2 位图地址

find_bootmap_pfn(int node, struct meminfo *mi, unsigned int bootmap_pages)

 

 start_pfn   = PAGE_ALIGN(__pa(&_end)) >> PAGE_SHIFT;

//内核数据的结尾4K对齐

 bootmap_pfn = 0;

 start = mi->bank[bank].start >> PAGE_SHIFT;

end = (mi->bank[bank].size +mi->bank[bank].start) >> PAGE_SHIFT; 

//本开发板只有一个bank,所以start 0x3000 0000  

if (end < start_pfn)

                   continue;

            if (start < start_pfn)

                   start = start_pfn;

            if (end <= start)

                   continue;

            if (end - start >= bootmap_pages) {

                   bootmap_pfn = start;

                   break;

            }

     }

     if (bootmap_pfn == 0)

            BUG();

     return bootmap_pfn;    

//返回值即为位图所在的地址

/////////////////////////////////////////

4.1.3 位图初始化

init_bootmem_node/init_bootmem_core

初始化数据结构:

typedef struct bootmem_data {

     unsigned long node_boot_start; // 0x3000 0000

     unsigned long node_low_pfn;  // 0x34000

     void *node_bootmem_map;   // phys_to_virt(bootmap_pfn << PAGE_SHIFT); 位图所在的地址;

     unsigned long last_offset;

     unsigned long last_pos;

     unsigned long last_success;   /* Previous allocation point.  To speed

                                  * up searching */

     struct list_head list;

} bootmem_data_t;

//////////////////////////////////

4.1.4 用位图分配页描述符

比较重要的函数:

free_area_init_node/ alloc_node_mem_map

                / free_area_init_core

 

Alloc_node_mem_map()

     size =  (end - start) * sizeof(struct page);

//size =( (0x3000 0000 >>PAGE_SHIFT) - (0x3400 0000>>PAGE_SHIFT))*sizeof(page)

//sizeof(page) = 32, 所以总数为0x80000

     map = alloc_bootmem_node(pgdat, size);

     pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);

     if (pgdat == NODE_DATA(0)) {

            mem_map = NODE_DATA(0)->node_mem_map;

mem_map即为所有内存描述符的数组

   

free_area_init_core()

该函数初始化pglist_data *pgdat->zone的各个字段;

init_currently_empty_zone()/ zone_init_free_lists()

   int order, t;

   for_each_migratetype_order(order, t) {

          INIT_LIST_HEAD(&zone->free_area[order].free_list[t]);

          zone->free_area[order].nr_free = 0;//初始化空闲链表,此时伙伴系统内存为空

   }

4.2 位图内存分配的退出和伙伴系统的激活

位图分配方式的退出在函数start_kernel/mem_init()中实现,系统内存分配的方式转到伙伴系统。

mem_init()

    free_unused_memmap_node

free_all_bootmem_node()/ free_all_bootmem_core()释放位图那部分内存

 

free_unused_memmap_node()

  /* 该函数的作用

  *If we had a previous bank, and there is a space

  * between the current bank and the previous, free it.

  */

 

free_all_bootmem_node()

{

  register_page_bootmem_info_node(pgdat);//此处该函数为空

  return free_all_bootmem_core(pgdat);//该函数完成两件事情

                  1.遍历位图表,释放内存至伙伴系统

                  2.释放位图表本身,将该表占用的内存释放到伙伴系统 

}

4.3 页描述符和页框的相互转换

FS2410开发板的内存空间是物理地址 0x3000 0000 --0x3400 0000,每个页框的大小为4k(即12位), 那么总的页框个数是 (0x3400 0000 - 0x3000 0000)>>PAGE_SHIFT = 0x400 0000 >> 12 = 0x4000;所以页描述符数组mem_map[]的总长度是 0x4000*sizeof(page)=0x4000*32=0x80000,该mem_map描述了从内存地址开始0x3000 0000到内存地址0x3400 0000结束的所有页框,也描述了已经被内核所占用的空间。

 

虚拟地址与物理地址之间的转换

#define __virt_to_phys(x)     ((x) - PAGE_OFFSET + PHYS_OFFSET)

#define __phys_to_virt(x)     ((x) - PHYS_OFFSET + PAGE_OFFSET)

 //                                    0x3000 0000    0xc000 0000

 

#define __pa(x)                    __virt_to_phys((unsigned long)(x))

#define __va(x)                    ((void *)__phys_to_virt((unsigned long)(x)))

#define pfn_to_kaddr(pfn)     __va((pfn) << PAGE_SHIFT) 

 

#define virt_to_page(kaddr)   pfn_to_page(__pa(kaddr) >> PAGE_SHIFT)

#define page_to_phys(page)  (page_to_pfn(page) << PAGE_SHIFT)

 

 

struct page *pfn_to_page(unsigned long pfn)

{

       return __pfn_to_page(pfn);

}

unsigned long page_to_pfn(struct page *page)

{

       return __page_to_pfn(page);

}

 

#define __pfn_to_page(pfn)   (mem_map + ((pfn) - ARCH_PFN_OFFSET)) 

#define __page_to_pfn(page) ((unsigned long)((page) - mem_map) + ARCH_PFN_OFFSET)

//  ARCH_PFN_OFFSET = (PHYS_OFFSET >> PAGE_SHIFT)

你可能感兴趣的:(Linux的内存初始化)