void __init build_all_zonelists(void)
{
int i;
for(i = 0 ; i < numnodes ; i++)
build_zonelists(NODE_DATA(i));//NODE_DATA(i)宏定义可以找到i号内存node对应的discontig_node_data[i]
printk("Built %i zonelists/n", numnodes);
}
build_zonelists()函数调用分两种请款,我们来浅析一下吧。
支持非统一内存访问(即配置了CONFIG_NUMA选项,该选项一般用于多处理器系统)
static void __init build_zonelists(pg_data_t *pgdat)
{
int i, j, k, node, local_node;
int prev_node, load;
struct zonelist *zonelist;//先要介绍一个结构体--struct zonelist. struct zonelist {
//struct zone *zones[MAX_NUMNODES * MAX_NR_ZONES + 1]; };
//其实就是就是一个指针数组,这个数组里面的每个成员都是一个struct zone,最多可以容纳整个系统的
//所有页区数。
DECLARE_BITMAP(used_mask, MAX_NUMNODES);//这个宏定义其实就是申明一个used_mask[1]这样的数组。
/* initialize zonelists */
for (i = 0; i < GFP_ZONETYPES; i++) {
zonelist = pgdat->node_zonelists + i;//node_zonelists页区列表结构数组,其实就是上面介绍的zonelist结构类型。
//这个列表数组其实就只是有三个元素,分别是我们说的3类页区。可以看出这里只
//是清零初始化。
memset(zonelist, 0, sizeof(*zonelist));
zonelist->zones[0] = NULL;
}
local_node = pgdat->node_id;
load = numnodes;
prev_node = local_node;
bitmap_zero(used_mask, MAX_NUMNODES);//这里是使16个node对应的used_mask[0]这个32位的相应位为0
while ((node = find_next_best_node(local_node, used_mask)) >= 0) {//对于find_next_best_node()函数的分析如下:
static int __init find_next_best_node(int node, void *used_node_mask)
{
int i, n, val;
int min_val = INT_MAX;
int best_node = -1;
for (i = 0; i < numnodes; i++) {
cpumask_t tmp;//这里是为每个处理器设置一位,来标记是否使用过,tmp里面的每一位都代表一个cpu。
n = (node+i)%numnodes;//求余,一上来肯定是以本次node开始的。
if (test_bit(n, used_node_mask))//因为是初始化函数调用的,所以都没有被使用过的。这里会跳过。
continue;
if (!test_bit(node, used_node_mask)) {//这里会执行。
best_node = node;
break;//找到没有被使用过的node就跳出上面的for循环。
}
val = node_distance(node, n);//如果发现的node不是从本次node开始的,val=1,否则就是0
tmp = node_to_cpumask(n);//由于这里注明是多个cpu的,所以如果是哪个node被使用了,对应的cpu也要标注一下,对应位变化
if (!cpus_empty(tmp))
val += PENALTY_FOR_NODE_WITH_CPUS;
if (val < min_val) {
min_val = val;
best_node = n;
}
}//上面涉及多个cpu时如何选取合适的node,本人现在未能理解。希望以后可以回来完善。
if (best_node >= 0)
set_bit(best_node, used_node_mask);//确实找到所需的node,这样在相应位置上1,表示这个node被使用了。
return best_node;
}//我们回到刚才的while,代码如下:
if (node_distance(local_node, node) !=node_distance(local_node, prev_node))
node_load[node] += load;
prev_node = node;
load--;//重点在后面拉。
for (i = 0; i < GFP_ZONETYPES; i++) {//开始为这个node的成员node_zonelist[]进行设置了。
zonelist = pgdat->node_zonelists + i;
for (j = 0; zonelist->zones[j] != NULL; j++);//寻找到一个不指向空的struct zone的结构体,记录这个J。
k = ZONE_NORMAL;
if (i & __GFP_HIGHMEM)//node_zonelist[2]存放highMem类型页区的strcut zone结构体
k = ZONE_HIGHMEM;
if (i & __GFP_DMA)//node_zonrlist[1]存放dma类型页区的strcut zone结构体
k = ZONE_DMA;
j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);//可以看出第一次调用此node时,把本次node各类页
//区对应的struct zone链接到各类node_zonelist[]上
//等下一次while的时候,我们就把下个node的各类页区
//加载上一次的后面。
zonelist->zones[j] = NULL;//每次都使其尾部指向null。以便下次利用。
}
}
}
下种情况是内存统一的,个人认为,如果只有一个cpu的话,就是使用一个统一的内存。我们用arm的大多数是使用下面这种。
static void __init build_zonelists(pg_data_t *pgdat)
{
int i, j, k, node, local_node;
local_node = pgdat->node_id;
for (i = 0; i < GFP_ZONETYPES; i++) {
struct zonelist *zonelist;
zonelist = pgdat->node_zonelists + i;
memset(zonelist, 0, sizeof(*zonelist));
j = 0;
k = ZONE_NORMAL;
if (i & __GFP_HIGHMEM)
k = ZONE_HIGHMEM;
if (i & __GFP_DMA)
k = ZONE_DMA;
j = build_zonelists_node(pgdat, zonelist, j, k);//上面的容易理解,这里不多说了。
for (node = local_node + 1; node < numnodes; node++)
j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);//这里把本次node后面的node都往上加。
for (node = 0; node < local_node; node++)
j = build_zonelists_node(NODE_DATA(node), zonelist, j, k);//把前面的node也往上加。
zonelist->zones[j] = NULL;//结尾处附上NULL
}
}//可以看到,我们在哪个内存node都可以访问到其他node不同类型页区的struct zoon结构数据,靠的就是我们这里的主角node_zonelist。