内存管理 初始化(三)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;