快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADI bf561 DSP
优视BF561EVB开发板
uclinux-2008r1-rc8 (移植到vdsp5)
Visual DSP++ 5.0
欢迎转载,但请保留作者信息
在使用可用内存之前,内核必须首先计算有多少空闲页可用,并为每页都分配一个page结构体进行描述。这就是页表初始化做的工作。
在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 )而言,其值为0x3bff000。alloc_bootmem_pages函数将以页(4096字节)为单位分配指定大小的内存。
这里比较有意思的是最后一个调用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个元素,其值分别为0x3bff和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))
这个函数的实现在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是一个有两个元素的数组,其值为{0x3bff, 0},分别代表了ZONE_DMA和ZONE_NORMAL两个区域的页表数量;node_start_pfn为0;zholes_size为NULL。
calculate_node_totalpages函数将设置pgdat中node_spanned_pages和node_present_pages两个成员的值。最终pgdat->node_spanned_pages和pgdat->node_present_pages两个成员的值都将为SDRAM的页表数量,对于 64M 内存而言(实际限制为 60M ),其值为0x3bff。
alloc_node_mem_map这个函数将使用bootmem为pgdat->node_mem_map分配内存空间,记住,node_mem_map这个成员将指向page结构体数组的第一个元素。
此函数的实现在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_pages和pgdat->node_present_pages两个成员的值,在这里调用的zone_spanned_pages_in_node和zone_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_pages和pgdat->node_present_pages两个成员的值都将为SDRAM的页表数量,对于 64M 内存而言(实际限制为 60M ),其值为0x3bff。
此函数的实现为:
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_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分配内存空间,记住,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 )