DSP

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

2019-07-13 16:38发布

    快乐虾 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),其值将为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)