内存管理 初始化(三)before mm_init()

看到了mm_init(),期间将从bootmem迁移到伙伴系统,slab分配器也会建立。

在分析mm_init()之前,把setup_arch(&command_line)之后的函数分析了以下,详见注释。

start_kernel()                                                                                                     
    |---->page_address_init()
    |     考虑支持高端内存
    |     业务:初始化page_address_pool链表;
    |          将page_address_maps数组元素按索引降序插入
    |          page_address_pool链表; 
    |          初始化page_address_htable数组.
    | 
    |---->setup_arch(&command_line);
    |
    |---->setup_per_cpu_areas();
    |     为per-CPU变量分配空间
    |
    |---->build_all_zonelist()
    |     为系统中的zone建立后备zone的列表.
    |     2.6.34中的建立过程与《深入Linux内核架构》中p_134~p_135的图不符
    |     (即使是UMA也不同),书中讲述是每个zone都有自己的zonelist,
    |     2.6.34中对于UMA,所有zone的后备列表都在
    |     pglist_data->node_zonelists[0]中;
    |
    |     期间也对per-CPU变量boot_pageset做了初始化. 
    |
    |---->page_alloc_init()
         |---->hotcpu_notifier(page_alloc_cpu_notifier, 0);
         |     不考虑热插拔CPU 
         |       
    |---->pidhash_init()
    |     详见下文. 
    |     根据低端内存页数和散列度,分配hash空间,并赋予pid_hash
    |
    |---->vfs_caches_init_early()
          |---->dcache_init_early()
          |     dentry_hashtable空间,d_hash_shift, h_hash_mask赋值;
          |     同pidhash_init();
          |     区别:   
          |         散列度变化了(13 - PAGE_SHIFT);
          |         传入alloc_large_system_hash的最后参数值为0;
          |       
          |---->inode_init_early()
          |     inode_hashtable空间,i_hash_shift, i_hash_mask赋值;
          |     同pidhash_init();
          |     区别:
          |         散列度变化了(14 - PAGE_SHIFT);
          |         传入alloc_large_system_hash的最后参数值为0;
          |
void pidhash_init(void)                                                                                            
    |---->pid_hash = alloc_large_system_hash("PID", sizeof(*pid_hash), 
    |         0, 18, HASH_EARLY|HASH_SMALL, &pidhash_shift, NULL, 4096);
    |     根据nr_kernel_pages(低端内存的页数),分配哈希数组,以及各个哈希
    |     数组元素下的哈希链表的空间,原理如下:
    |     number = nr_kernel_pages; 
    |     number >= (18 - PAGE_SHIFT) 根据散列度获得数组元素个数
    |     number = roundup_pow_of_two(number);
    |     pidhash_shift = max{x | 2**x <= number}
    |     size = number * sizeof(*pid_hash);
    |     使用位图分配器分配size空间,将返回值付给pid_hash;
    |
    |---->pidhash_size = 1 << pidhash_shift;
    |
    |---->for(i = 0; i < pidhash_size; i++)
    |         INIT_HLIST_HEAD(&pid_hash[i]);
void build_all_zonelists(void)
    |---->set_zonelist_order()
         |---->current_zonelist_order = ZONELIST_ORDER_ZONE;
    |
    |---->__build_all_zonelists(NULL);
    |    Memory不支持热插拔, 为每个zone建立后备的zone,
    |    每个zone及自己后备的zone,形成zonelist
    |
    |---->vm_total_pages = nr_free_pagecache_pages();
    |    业务:获得所有zone中的present_pages总和.
   |
    |---->page_group_by_mobility_disabled = 0; | 对于代码中的判断条件一般不会成立,因为页数会最够多(内存较大)
static int __build_all_zonelists(void *dummy)
    |---->pg_data_t *pgdat = NULL;
    |     pgdat = &contig_page_data;(单node)
    |
    |---->build_zonelists(pgdat);
    |     为每个zone建立后备zone的列表
    |
    |---->build_zonelist_cache(pgdat);
          |---->pdat->node_zonelists[0].zlcache_ptr = NULL;
          |     UMA体系结构
          |
    |---->for_each_possible_cpu(cpu)
    |     setup_pageset(&per_cpu(boot_pageset, cpu), 0);
          |详见下文
void build_zonelists(pg_data *pgdat)
    |---->struct zonelist *zonelist = NULL;
    |     enum zone_type j;
    |     zonelist = &pgdat->node_zonelists[0];
    |
    |---->j = build_zonelists_node(pddat, zonelist, 0, MAX_NR_ZONES - 1);
    |     为pgdat->node_zones[0]建立后备的zone,node_zones[0]后备的zone
    |     存储在node_zonelist[0]内,对于node_zone[0]的后备zone,其后备的zone
    |     链表如下(只考虑UMA体系,而且不考虑ZONE_DMA):
    |     node_zonelist[0]._zonerefs[0].zone = &node_zones[2];
    |     node_zonelist[0]._zonerefs[0].zone_idx = 2;
    |     node_zonelist[0]._zonerefs[1].zone = &node_zones[1];
    |     node_zonelist[0]._zonerefs[1].zone_idx = 1;
    |     node_zonelist[0]._zonerefs[2].zone = &node_zones[0];
    |     node_zonelist[0]._zonerefs[2].zone_idx = 0;
    |     
    |     zonelist->_zonerefs[3].zone = NULL;
    |     zonelist->_zonerefs[3].zone_idx = 0;    
void setup_pageset(struct per_cpu_pageset *p, unsigned long batch)
    |---->memset(p, 0, sizeof(*p));
    |
    |---->struct per_cpu_pages *pcp = NULL;
    |     pcp = &p->pcp;
    |     pcp->count = 0;
    |     pcp->high = 6 * batch;
    |     pcp->batch = max(1UL, 1 * batch);
    |
    |---->for(migratetype = 0; 
    |         migratetype < MIGRATE_PCPTYPES;
    |         migratetype++) |---->INIT_LIST_HEAD(&pcp->lists[migratetype]);
unsigned int nr_free_pagecache_pages(void)
  |-->return nr_free_zone_pages(gfp_zone(GFP_HIGHUSER_MOVABLE));
             对于UMA,且不考虑ZONE_DMA,参数为2
             |-->参数offset = 2;
             |   struct zoneref *z =NULL;
             |   struct zone *zone = NULL;
             |   unsigned int sum = 0;
             |   struct zonelist *zonelist = 
             |         node_zonelist(numa_node_id(), GFP_KERNEL)
             |   对于UMA,zonelist = 
             |           (&contig_page_data)->node_zonelists; | |-->for_each_zone_zonelist(zone, z, zonelist, offset) | offset的作用在于遍历zonelist下的_zonerefs数组元数中, | zone_idx <= offset的zone; | 因此当offset为0时,遍历的结果相当于 | zone = &pglist_data->node_zones[0] | | unsigned long size = zone->present_pages; | 获得该zone跨越的页数. | | unsigned long high = high_wmark_pages(zone); | if(size > high) sum += size - high; | (high暂时为0,因为zone->watermark[WMARK_HIGH] = 0) | |-->return sum;
原文地址:https://www.cnblogs.com/openix/p/3341174.html