快乐虾
http://blog.csdn.net/lights_joy/
本文适用于
ADI bf561 DSP
优视BF561EVB开发板
uclinux-2008r1-rc8 (移植到vdsp5)
Visual DSP++ 5.0
欢迎转载,但请保留作者信息
在pglist_data这个结构体中,有一个成员:
struct zonelist node_zonelists[MAX_NR_ZONES];
下面就来看看它的初始化过程。
在start_kernel函数中调用了这个函数:
build_all_zonelists();
它的实现在mm/page_alloc.c中:
void __meminit build_all_zonelists(void)
{
if (system_state == SYSTEM_BOOTING) {
__build_all_zonelists(NULL);
cpuset_init_current_mems_allowed();
} else {
// /* we have to stop all cpus to guaranntee there is no user
// of zonelist */
// stop_machine_run(__build_all_zonelists, NULL, NR_CPUS);
// /* cpuset refresh routine should be here */
WARN();
}
vm_total_pages = nr_free_pagecache_pages();
printk("Built %i zonelists. Total pages: %ld/n",
num_online_nodes(), vm_total_pages);
}
对于这个函数,只会在start_kernel中调用一次,此时system_state == SYSTEM_BOOTING。当然,如果需要支持memory_hot_plug,还会有另外的调用,在此忽略它,因此实际只会执行if的第一个分支。
在这个函数中对cpuset_init_current_mems_allowed的调用什么事也不做。
此函数实现为:
/* return values int ....just for stop_machine_run() */
static int __meminit __build_all_zonelists(void *dummy)
{
int nid;
for_each_online_node(nid) {
build_zonelists(NODE_DATA(nid));
build_zonelist_cache(NODE_DATA(nid));
}
return 0;
}
在这个函数中for_each_online_node只会执行一次,因为在整个系统中,只有唯一一个pglist_data!
static void __meminit build_zonelists(pg_data_t *pgdat)
{
int node, local_node;
enum zone_type i,j;
local_node = pgdat->node_id;
for (i = 0; i < MAX_NR_ZONES; i++) {
struct zonelist *zonelist;
zonelist = pgdat->node_zonelists + i;
j = build_zonelists_node(pgdat, zonelist, 0, i);
/*
* Now we build the zonelist so that it contains the zones
* of all the other nodes.
* We don't want to pressure a particular node, so when
* building the zones for node N, we make sure that the
* zones coming right after the local ones are those from
* node N+1 (modulo N)
*/
for (node = local_node + 1; node < MAX_NUMNODES; node++) {
if (!node_online(node))
continue;
j = build_zonelists_node(NODE_DATA(node), zonelist, j, i);
}
for (node = 0; node < local_node; node++) {
if (!node_online(node))
continue;
j = build_zonelists_node(NODE_DATA(node), zonelist, j, i);
}
zonelist->zones[j] = NULL;
}
}
在这个函数中有
#define NODES_SHIFT 0
#define MAX_NUMNODES (1 << NODES_SHIFT)
而localnode的值为0。因此这个函数实际上就相当于:
static void __meminit build_zonelists(pg_data_t *pgdat)
{
int node, local_node;
enum zone_type i,j;
local_node = pgdat->node_id;
for (i = 0; i < MAX_NR_ZONES; i++) {
struct zonelist *zonelist;
zonelist = pgdat->node_zonelists + i;
j = build_zonelists_node(pgdat, zonelist, 0, i);
zonelist->zones[j] = NULL;
}
}
实际上,我们只要关心build_zonelists_node函数就行了。
/*
* Builds allocation fallback zone lists.
*
* Add all populated zones of a node to the zonelist.
*/
static int __meminit build_zonelists_node(pg_data_t *pgdat,
struct zonelist *zonelist, int nr_zones, enum zone_type zone_type)
{
struct zone *zone;
BUG_ON(zone_type >= MAX_NR_ZONES);
zone_type++;
do {
zone_type--;
zone = pgdat->node_zones + zone_type;
if (populated_zone(zone)) { // 只要present_pages不为,则此条件为真
zonelist->zones[nr_zones++] = zone;
check_highest_zone(zone_type); // 空调用
}
} while (zone_type);
return nr_zones;
}
在内核中有两个ZONE,ZONE_DMA和ZONE_NORMAL,但是ZONE_NORMAL的内存大小为0,其present_pages也为0,因此在初始化后,zonelist->zones数组实际只有一个元素,它指向ZONE_DMA,即contig_page_data->zone[0]。
这个函数仅在__build_all_zonelists中被调用一次:
/* non-NUMA variant of zonelist performance cache - just NULL zlcache_ptr */
static void __meminit build_zonelist_cache(pg_data_t *pgdat)
{
int i;
for (i = 0; i < MAX_NR_ZONES; i++)
pgdat->node_zonelists[i].zlcache_ptr = NULL;
}
很简单,没什么可说的。
这个函数的实现为:
/*
* Amount of free RAM allocatable within all zones
*/
unsigned int nr_free_pagecache_pages(void)
{
return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER));
}
在这里gfp_zone(GFP_HIGHUSER)将返回GFP_HIGHUSER所在的内存区域,因为内核只使用ZONE_DMA,故这个调用返回0,即ZONE_DMA。
下面看看nr_free_zone_pages的实现:
static unsigned int nr_free_zone_pages(int offset)
{
/* Just pick one node, since fallback list is circular */
pg_data_t *pgdat = NODE_DATA(numa_node_id());
unsigned int sum = 0;
struct zonelist *zonelist = pgdat->node_zonelists + offset;
struct zone **zonep = zonelist->zones;
struct zone *zone;
for (zone = *zonep++; zone; zone = *zonep++) {
unsigned long size = zone->present_pages;
unsigned long high = zone->pages_high;
if (size > high)
sum += size - high;
}
return sum;
}
传递进来的参数为0,而且我们知道zonelist->zones实际只有一个元素,且指向pgdat->node_zones[0],即ZONE_DMA的描述结构zone。因而这个函数的功能就简单了,就是返回ZONE_DMA的空闲页数。对于64M内存(限制为60M),其值将为0x3b6a。
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)
再读uclinux-2008r1(bf561)内核存储区域管理(2):可用页表初始化(2008/7/25)
再读uclinux-2008r1(bf561)内核存储区域管理(3):zone初始化(2008-7-25)