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

  在对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()分析完了。

 

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