再读uclinux-2008r1(bf561)内核存储区域管理(4):zonelist初始化

 

 

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

   

 

本文适用于

ADI bf561 DSP

优视BF561EVB开发板

uclinux-2008r1-rc8 (移植到vdsp5)

Visual DSP++ 5.0

   

 

欢迎转载,但请保留作者信息

 

 

1.1.1   zonelist初始化

pglist_data这个结构体中,有一个成员:

     struct zonelist node_zonelists[MAX_NR_ZONES];

下面就来看看它的初始化过程。

1.1.1.1             build_all_zonelists

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的调用什么事也不做。

1.1.1.2             __build_all_zonelists

此函数实现为:

/* 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!

 

1.1.1.3             build_zonelists

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;

}

在内核中有两个ZONEZONE_DMAZONE_NORMAL,但是ZONE_NORMAL的内存大小为0,其present_pages也为0,因此在初始化后,zonelist->zones数组实际只有一个元素,它指向ZONE_DMA,即contig_page_data->zone[0]

 

1.1.1.4             build_zonelist_cache

这个函数仅在__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;

}

很简单,没什么可说的。

 

1.1.1.5             nr_free_pagecache_pages

这个函数的实现为:

/*

 * 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 ),其值将为0x3b 6a

 

 

参考资料

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 )

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

嵌云阁主

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值