再读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
欢迎转载,但请保留作者信息
在使用可用内存之前,内核必须首先计算有多少空闲页可用,并为每页都分配一个 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 结构体数组的第一个元素。
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_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 。
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_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)
打开微信“扫一扫”,打开网页后点击屏幕右上角分享按钮