Linux中的内存分配和释放之build_all_zonelists()函数分析

  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。

                  

你可能感兴趣的:(linux,内存管理)