在对free_area_init_node()函数分析之前,我们也要看看它的源头,这个函数的调用过程如下start_kernel()->paging_init()->free_area_init_node(),我们来看看在调用这个函数之前,在paging_init()前面的语句做了些什么。
/*
* initialise the zones within each node
*/
for (node = 0; node < numnodes; node++) {
unsigned long zone_size[MAX_NR_ZONES];//存放这个内存node的全部内存页数。
unsigned long zhole_size[MAX_NR_ZONES];//存放这次内存node的孔洞内存页数。(每个成员对应该内存node的一个页区)
struct bootmem_data *bdata;
pg_data_t *pgdat;
int i;
/*
* Initialise the zone size information.
*/
for (i = 0; i < MAX_NR_ZONES; i++) {
zone_size[i] = 0;
zhole_size[i] = 0;
}//对这次内存node的最多3个内存页区内存页数和孔洞内存页数清零。
pgdat = NODE_DATA(node);//获得这个node对应的disconting_node_data[n]
bdata = pgdat->bdata;
/*
* The size of this node has already been determined.
* If we need to do anything fancy with the allocation
* of this memory to the zones, now is the time to do
* it.
*/
zone_size[0] = bdata->node_low_pfn -
(bdata->node_boot_start >> PAGE_SHIFT);//获得本次内存node的全部页数
/*
* If this zone has zero size, skip it.
*/
if (!zone_size[0])
continue;
/*
* For each bank in this node, calculate the size of the
* holes. holes = node_size - sum(bank_sizes_in_node)
*/
zhole_size[0] = zone_size[0];
for (i = 0; i < mi->nr_banks; i++) {
if (mi->bank[i].node != node)
continue;
zhole_size[0] -= mi->bank[i].size >> PAGE_SHIFT;
}//上面这个循环可以很容易弄明白zhole_size[0]存放着本次内存node的内存孔洞的页数。
/*
* Adjust the sizes according to any special
* requirements for this machine type.
*/
arch_adjust_zones(node, zone_size, zhole_size);//这是一个空函数。
free_area_init_node(node, pgdat, zone_size,
bdata->node_boot_start >> PAGE_SHIFT, zhole_size);
}
void __init free_area_init_node(int nid, struct pglist_data *pgdat,
unsigned long *zones_size, unsigned long node_start_pfn,
unsigned long *zholes_size)
{
pgdat->node_id = nid;
pgdat->node_start_pfn = node_start_pfn;
calculate_zone_totalpages(pgdat, zones_size, zholes_size);//求出本节点所有内存页区的页数总和(包括内存孔洞的),所有内存页区的实际页数(不包括内存孔洞的)。
if (!pfn_to_page(node_start_pfn))//我们来看看它的宏定义:
//#define pfn_to_page(pfn) ((mem_map + (pfn)) - PHYS_PFN_OFFSET),按注释来说
//这个宏是通过pfn(这是物理页号)来求得对应的struct page结构体所在的存储单元。
//mem_map表示这个节点所有页的struct page结构体的空间起始位置,PHYS_PFN_OFFSET
//是RAM空间的起始页号,通过上面的计算就可以求得对应页的struct page结构体的地址。
//如果地址为空,就说明没有为其分配空间了。
node_alloc_mem_map(pgdat);//为该内存node的所有内存页的strcut page结构体申请空间,我们是从DMA内存空间之后的空
//间开始申请的,申请空间按cache line (cache块大小=32)的方式对其的。
free_area_init_core(pgdat, zones_size, zholes_size);//这个是核心函数,等下我们会把它展开细讲的,这个函数主要就是完成
//本次内存node的内存页区的struct zoon结构体成员的设置。
}
static void __init free_area_init_core(struct pglist_data *pgdat,
unsigned long *zones_size, unsigned long *zholes_size)
{
unsigned long i, j;
const unsigned long zone_required_alignment = 1UL << (MAX_ORDER-1);
int cpu, nid = pgdat->node_id;
unsigned long zone_start_pfn = pgdat->node_start_pfn;
pgdat->nr_zones = 0;//将该内存节点的内存页区数清0.
init_waitqueue_head(&pgdat->kswapd_wait);//初始化内存节点的内存置换等待队列。
for (j = 0; j < MAX_NR_ZONES; j++) {//对每个节点的每个页区进行操作。
struct zone *zone = pgdat->node_zones + j;//让zone作为本次内存节点每个页区的结构指针。
unsigned long size, realsize;
unsigned long batch;
zone_table[NODEZONE(nid, j)] = zone;//把内存node的各个页区的指针结构保留在全局指针数组zone_table[n]里。
realsize = size = zones_size[j];
if (zholes_size)
realsize -= zholes_size[j];
if (j == ZONE_DMA || j == ZONE_NORMAL)
nr_kernel_pages += realsize;//nr_kernel_pages是全局变量,保存着所有内存node的dma和normal页区的实际页数。
nr_all_pages += realsize;//nr_all_pages是全局变量,保存着每个内存node的3个页区的实际存在的页数。
zone->spanned_pages = size;//该页区内的总页数。
zone->present_pages = realsize;//该页区内的实际页数。
zone->name = zone_names[j];//该页区的明“dma, normal, highMem”
spin_lock_init(&zone->lock);
spin_lock_init(&zone->lru_lock);//页区的锁。
zone->zone_pgdat = pgdat;//页区所属内存节点的内存节点数据结构。
zone->free_pages = 0;//该页区的空闲页数。
zone->temp_priority = zone->prev_priority = DEF_PRIORITY;//页区临时和预置优先级别都是DEF_PRIORITY=12.
/*
* The per-cpu-pages pools are set to around 1000th of the
* size of the zone. But no more than 1/4 of a meg - there's
* no point in going beyond the size of L2 cache.
*
* OK, so we don't know how big the cache is. So guess.
*/
batch = zone->present_pages / 1024;
if (batch * PAGE_SIZE > 256 * 1024)
batch = (256 * 1024) / PAGE_SIZE;
batch /= 4;//要求最大不可以超过16
if (batch < 1)
batch = 1;//小于1的话就附上1.
for (cpu = 0; cpu < NR_CPUS; cpu++) {//设置本页区中每个处理器的冷区和热区内存数据结构。
struct per_cpu_pages *pcp;
pcp = &zone->pageset[cpu].pcp[0]; //每个页区都为每个处理器维护一组高速缓存内存--struct per_cpu_pageset
//pageset[]。每个pageset[]包含一个struct per_cpu_pages pcp[2]数组
//这里的pcp[0]代表热区高速缓存内存。
pcp->count = 0;//per_cpu_pages这个结构体是描述一些空闲页的,count就代表当前空闲页数。
pcp->low = 2 * batch;//这是current的下限原则。
pcp->high = 6 * batch;//这是current得上限原则。
pcp->batch = 1 * batch;
INIT_LIST_HEAD(&pcp->list);//初始化一个链表,这个是用来连接pcp高速缓存内存所指的那些内存页的struct page
pcp = &zone->pageset[cpu].pcp[1]; /* cold */
pcp->count = 0;
pcp->low = 0;
pcp->high = 2 * batch;
pcp->batch = 1 * batch;
INIT_LIST_HEAD(&pcp->list);//上面的几行是设置冷区的,不同的地方在于low,high
}
printk(KERN_DEBUG " %s zone: %lu pages, LIFO batch:%lu/n",
zone_names[j], realsize, batch);
INIT_LIST_HEAD(&zone->active_list);//该内存页区中以激活的内存页的描述结构中的lru会连接到这个链表中的。
INIT_LIST_HEAD(&zone->inactive_list);//这里和上述的意义是相反的,这里是未激活的。
zone->nr_scan_active = 0;//扫描激活内存页数。
zone->nr_scan_inactive = 0;
zone->nr_active = 0;//激活的内存页数。
zone->nr_inactive = 0;
if (!size)
continue;//如果该页区的内存页数为0时,不考虑这个页区。
/*
* The per-page waitqueue mechanism uses hashed waitqueues
* per zone.
*/
zone->wait_table_size = wait_table_size(size);//zone->wait_table_size表示等待队列的大小,其实就是等待队列的
//个数,一般PAGES_PER_WAITQUEUE(256)页为一个等待队列,
//等待队列的个数介于0~4096,同时也要求必须是2的N次幂。
zone->wait_table_bits =
wait_table_bits(zone->wait_table_size);//其实就是求出上面语句2的N次幂中的"1"所在的位号。
zone->wait_table = (wait_queue_head_t *)
alloc_bootmem_node(pgdat, zone->wait_table_size
* sizeof(wait_queue_head_t));//wait_table_size个wait_queue_head_t结构申请了空间,返回的地址放在
//wait_table。一个wait_queue_head_t可以构成一个链表。
for(i = 0; i < zone->wait_table_size; ++i)
init_waitqueue_head(zone->wait_table + i);//初始化wait_queue_head_t这个结构体的锁和链表。
pgdat->nr_zones = j+1;//统计这个内存node的页区数
zone->zone_mem_map = pfn_to_page(zone_start_pfn);//保存这个内存页区struct page结构空间的起始地址。
zone->zone_start_pfn = zone_start_pfn;//该内存页区的物理起始页号(会发现这个和本次节点的物理起始页号相同。)
if ((zone_start_pfn) & (zone_required_alignment-1))//可以看出这里要求这个物理地址要按2exp(max_order)对齐
printk("BUG: wrong zone alignment, it will crash/n");
memmap_init(size, nid, j, zone_start_pfn);//该内存页区中每页内存对应的struct page结构设置一下。具体设置如下:
//1.该内存页区在全局变量zone_table[]数组中的下标号存放到该页区所有页
//的struct page结构体中标志字flags的最高若干位。
//2.page->_count=-1;page->_mapcount=-1;设置标志字flags中
//PG_reserved=1这样是为了保护内存页不被释放掉。
zone_start_pfn += size;//计算下一个内存页区的开始页的页号。
zone_init_free_lists(pgdat, zone, zone->spanned_pages);//初始化这个内存页区对应的struct zone的成员
//free_area[MAX_ORDER]数组,主要干如下事情:
//1.初始化free_area[order].free_list链表
//2.为每个order的伙伴位码表申请空间,空间地址存放在
//free_area[order].map处。
}
}
好了,这样我就把free_area_init_node()分析完了。