转载: linux 内存管理 - paging_init 函数

来源:互联网 发布:600588用友网络 编辑:程序博客网 时间:2024/05/17 00:18
转载自: http://blog.csdn.net/decload/article/details/8080126
  

linux 内存管理 - paging_init 函数

源码为 2.6.37内核,x86_64架构,内存模型为Sparse Memory


paging_init函数在setup_arch函数中被调用,用于初始化所有节点的pg_data_t结构,以及节点对应的管理区zone结构,和page结构。


调用的大致过程为:

start_kernel()
   --> setup_arch()
      --> paging_init()
         --> free_area_init_nodes()
            --> free_area_init_node()
               --> free_area_init_core()
                  --> memmap_init()



具体如下:

paging_init()在setup_arch()中被调用,定义为:

[cpp] view plaincopy
  1. void __init paging_init(void)  
  2. {  
  3.         unsigned long max_zone_pfns[MAX_NR_ZONES]; /* MAX_NR_ZONES = 4 */  
  4.   
  5.         memset(max_zone_pfns, 0, sizeof(max_zone_pfns));  
  6.         max_zone_pfns[ZONE_DMA] = MAX_DMA_PFN; /* 16M */  
  7.         max_zone_pfns[ZONE_DMA32] = MAX_DMA32_PFN; /* 4G */  
  8.         max_zone_pfns[ZONE_NORMAL] = max_pfn; /* 最大物理内存 */  
  9.   
  10.         /* 为所有node所拥有的物理page建立mem_sections  
  11.          * 将放入early_node_map中所有节点的memory region的page放入另外一个mem_section的数组中。 
  12.          * 使用mem_section目的可能是内存分配更加高效。 
  13.          * kernel中一个mem_section包含了物理地址相连的固定数目个page。 
  14.          * 给定一个物理地址,我们可以得到它所在的page,也能得到它所在的mem_section的下标。 
  15.          * 而sparse_memory_present_with_active_regions的作用就是为给定node所拥有的物理page建立mem_sections. 
  16.          */  
  17.         sparse_memory_present_with_active_regions(MAX_NUMNODES);  
  18.         sparse_init();  
  19.   
  20.         /*    
  21.          * clear the default setting with node 0 
  22.          * note: don't use nodes_clear here, that is really clearing when 
  23.          *       numa support is not compiled in, and later node_set_state 
  24.          *       will not set it back. 
  25.          */  
  26.         node_clear_state(0, N_NORMAL_MEMORY);  
  27.   
  28.         /* 初始化所有pg_data_t和zone、page的数据 */  
  29.         free_area_init_nodes(max_zone_pfns);  
  30. }  

paging_init()调用了free_area_init_nodes函数初始化所有结点的pg_data_t和zone、page的数据,并打印了管理区信息:

[cpp] view plaincopy
  1. /** 
  2.  * free_area_init_nodes - Initialise all pg_data_t and zone data 
  3.  * @max_zone_pfn: an array of max PFNs for each zone 
  4.  * 
  5.  * This will call free_area_init_node() for each active node in the system. 
  6.  * Using the page ranges provided by add_active_range(), the size of each 
  7.  * zone in each node and their holes is calculated. If the maximum PFN 
  8.  * between two adjacent zones match, it is assumed that the zone is empty. 
  9.  * For example, if arch_max_dma_pfn == arch_max_dma32_pfn, it is assumed 
  10.  * that arch_max_dma32_pfn has no pages. It is also assumed that a zone 
  11.  * starts where the previous one ended. For example, ZONE_DMA32 starts 
  12.  * at arch_max_dma_pfn. 
  13.  */  
  14. /* 初始化各个节点的所有pg_data_t和zone、page的数据 */  
  15. void __init free_area_init_nodes(unsigned long *max_zone_pfn)  
  16. {  
  17.         unsigned long nid;   
  18.         int i;  
  19.   
  20.         /* Sort early_node_map as initialisation assumes it is sorted */  
  21.         sort_node_map();  
  22.   
  23.         /*    
  24.          * 以下设置arch_zone_lowest_possible_pfn和arch_zone_highest_possible_pfn为各个管理区的边界 
  25.          * 即 arch_zone_lowest_possible_pfn  = {最低pfn-0或64K, 16M   , 4G     , 0      } 
  26.          *    arch_zone_highest_possible_pfn = {     16M      , 4G    , max_pfn, 0      } 
  27.          *                                     {     DMA      , DMA_32, NORMAL , MOVABLE} 
  28.          */  
  29.         /* Record where the zone boundaries are */  
  30.     memset(arch_zone_lowest_possible_pfn, 0,  
  31.                                 sizeof(arch_zone_lowest_possible_pfn));  
  32.         memset(arch_zone_highest_possible_pfn, 0,  
  33.                                 sizeof(arch_zone_highest_possible_pfn));  
  34.         /* 查找early_node_map中的最低pfn */  
  35.         arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions();  
  36.         arch_zone_highest_possible_pfn[0] = max_zone_pfn[0]; /* max_zone_pfn = {16M, 4G, max_pfn} */  
  37.         for (i = 1; i < MAX_NR_ZONES; i++) {  
  38.                 if (i == ZONE_MOVABLE)  
  39.                         continue;  
  40.                 arch_zone_lowest_possible_pfn[i] =  
  41.                         arch_zone_highest_possible_pfn[i-1];  
  42.                 arch_zone_highest_possible_pfn[i] =  
  43.                         max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);  
  44.         }  
  45.         arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0; /* 现在arch_zone[ZONE_MOVABLE]暂时是空的 */  
  46.         arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;  
  47.   
  48.         /* Find the PFNs that ZONE_MOVABLE begins at in each node */  
  49.         /*  为每一个cpu node建立zone_movable_pfn。 
  50.          *  ZONE_MOVABLE是在kernel启动时由命令行传入的参数, 
  51.          *  意义在于指明内核空间中哪些page是可以移动的, 
  52.          *  其他的内核page则称为kernel core,是不可以移动的。 
  53.      *  find_zone_movable_pfns_for_nodes的作用就是按照 
  54.          *  early_node_map根据每个node的不同内存分布计算出 
  55.          *  每一个node中movable page的数量. 
  56.          */  
  57.         memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));  
  58.         find_zone_movable_pfns_for_nodes(zone_movable_pfn);  
  59.   
  60.         /* 打印DMA、DMA_32、ZONE_NORMAL的信息 */  
  61.         /* Print out the zone ranges */  
  62.         printk("Zone PFN ranges:\n");  
  63.         for (i = 0; i < MAX_NR_ZONES; i++) {  
  64.                 if (i == ZONE_MOVABLE)  
  65.                         continue;  
  66.                 printk("  %-8s ", zone_names[i]);  
  67.                 if (arch_zone_lowest_possible_pfn[i] ==  
  68.                                 arch_zone_highest_possible_pfn[i])  
  69.                         printk("empty\n");  
  70.                 else  
  71.                         printk("%0#10lx -> %0#10lx\n",  
  72.                                 arch_zone_lowest_possible_pfn[i],  
  73.                                 arch_zone_highest_possible_pfn[i]);  
  74.         }  
  75.   
  76.         /* 打印ZONE_MOVABLE的信息 */  
  77.         /* Print out the PFNs ZONE_MOVABLE begins at in each node */  
  78.         printk("Movable zone start PFN for each node\n");  
  79.         for (i = 0; i < MAX_NUMNODES; i++) {  
  80.                 if (zone_movable_pfn[i])  
  81.                         printk("  Node %d: %lu\n", i, zone_movable_pfn[i]);  
  82.         }  
  83.   
  84.     /* 打印 early_node_map 数组的信息 */  
  85.         /* Print out the early_node_map[] */  
  86.         printk("early_node_map[%d] active PFN ranges\n", nr_nodemap_entries);  
  87.         for (i = 0; i < nr_nodemap_entries; i++)  
  88.                 printk("  %3d: %0#10lx -> %0#10lx\n", early_node_map[i].nid,  
  89.                                                 early_node_map[i].start_pfn,  
  90.                                                 early_node_map[i].end_pfn);  
  91.   
  92.         /* Initialise every node */  
  93.         mminit_verify_pageflags_layout();  
  94.         setup_nr_node_ids();  
  95.   
  96.         /* 对所有节点循环 */  
  97.         for_each_online_node(nid) {  
  98.                 pg_data_t *pgdat = NODE_DATA(nid); /* 获得节点的pg_data_t结构 */  
  99.   
  100.                 /* 调用free_area_init_node初始化节点nid对应的pg_data_t和zone、page的数据 */  
  101.                 free_area_init_node(nid, NULL,  
  102.                                 find_min_pfn_for_node(nid), NULL); /* find_min_pfn_for_node函数从 
  103.                                                         early_node_map数组中找出该节点的最低pfn */  
  104.   
  105.                 /* Any memory on that node */  
  106.                 if (pgdat->node_present_pages)  
  107.                         node_set_state(nid, N_HIGH_MEMORY);  /* 设置该节点拥有regular memory */  
  108.                 check_for_regular_memory(pgdat);          /* 没定义CONFIG_HIGHMEM函数为空 */  
  109.         }  
  110. }  



而在free_area_init_nodes函数中通过循环遍历各个节点,循环中调用了free_area_init_node函数初始化该节点对应的pg_data_t和zone、page的数据 :


[cpp] view plaincopy
  1. /* 初始化节点nid对应的pg_data_t和zone、page的数据 
  2.  * @ nid 为节点标识符 
  3.  * @ zone_size 为null 
  4.  * @ node_start_pfn 为nid节点的起始pfn 
  5.  * @ zholes_size 为null 
  6.  */       
  7. void __paginginit free_area_init_node(int nid, unsigned long *zones_size,  
  8.                 unsigned long node_start_pfn, unsigned long *zholes_size)  
  9. {         
  10.         pg_data_t *pgdat = NODE_DATA(nid); /* 获得该节点的pg_data_t结构 */  
  11.   
  12.         pgdat->node_id = nid; /* 设置节点标识符 */  
  13.         pgdat->node_start_pfn = node_start_pfn; /* 设置节点中第一个页框的下标 */  
  14.   
  15.         /* 计算对于该node来说有多少pages可用, 
  16.          * 设置pg_data_t中node_spanned_pages成员为节点总的页框数,包括洞 
  17.          *                node_present_pages成员为总的页框数,不包括洞 
  18.          */  
  19.         calculate_node_totalpages(pgdat, zones_size, zholes_size);  
  20.   
  21.         /* 在没有定义CONFIG_FLAT_NODE_MEM_MAP的情况下,此函数没作用 */  
  22.         alloc_node_mem_map(pgdat);  
  23. #ifdef CONFIG_FLAT_NODE_MEM_MAP  
  24.         printk(KERN_DEBUG "free_area_init_node: node %d, pgdat %08lx, node_mem_map %08lx\n",  
  25.                 nid, (unsigned long)pgdat,  
  26.                 (unsigned long)pgdat->node_mem_map);  
  27. #endif  
  28.   
  29.         /* 调用free_area_init_core继续初始化pg_data_t结构,初始化zone以及page结构 */  
  30.         free_area_init_core(pgdat, zones_size, zholes_size);  
  31. }  


继续调用free_area_init_core函数,继续初始化该节点的pg_data_t结构,初始化zone以及page结构 ,

free_area_init_core函数是初始化zone的核心:


[cpp] view plaincopy
  1. /* 
  2.  * Set up the zone data structures: 
  3.  *   - mark all pages reserved 
  4.  *   - mark all memory queues empty 
  5.  *   - clear the memory bitmaps 
  6.  */  
  7. /* 继续初始化pg_data_t结构,初始化zone以及page结构 */  
  8. static void __paginginit free_area_init_core(struct pglist_data *pgdat,  
  9.                 unsigned long *zones_size, unsigned long *zholes_size)  
  10. {  
  11.         enum zone_type j;  
  12.         int nid = pgdat->node_id;  
  13.         unsigned long zone_start_pfn = pgdat->node_start_pfn;  
  14.         int ret;  
  15.   
  16.         pgdat_resize_init(pgdat); /* 初始化pgdat->node_size_lock自旋锁 */  
  17.         pgdat->nr_zones = 0;  
  18.         init_waitqueue_head(&pgdat->kswapd_wait); /* 初始化pgdat->kswapd_wait等待队列 */  
  19.         pgdat->kswapd_max_order = 0; /* 初始化页换出守护进程创建空闲块的大小,为2^kswapd_max_order */  
  20.         pgdat_page_cgroup_init(pgdat); /* 空函数 */  
  21.   
  22.         /* 遍历每个管理区 */  
  23.         for (j = 0; j < MAX_NR_ZONES; j++) {  
  24.                 struct zone *zone = pgdat->node_zones + j;  
  25.                 unsigned long size, realsize, memmap_pages;  
  26.                 enum lru_list l;  
  27.   
  28.                 /* size为该管理区中的页框数,包括洞 */  
  29.                 size = zone_spanned_pages_in_node(nid, j, zones_size);  
  30.                 /* realsize为管理区中的页框数,不包括洞 */  
  31.                 realsize = size - zone_absent_pages_in_node(nid, j,  
  32.                                                                 zholes_size);  
  33.   
  34.         /* 
  35.                  * Adjust realsize so that it accounts for how much memory 
  36.                  * is used by this zone for memmap. This affects the watermark 
  37.                  * and per-cpu initialisations 
  38.                  */  
  39.                 /* 调整realsize的大小,即减去page结构体占用的内存大小 */  
  40.                 memmap_pages =  /* memmap_pags为包括洞的所有页框的page结构体所占的大小 */  
  41.                         PAGE_ALIGN(size * sizeof(struct page)) >> PAGE_SHIFT;  
  42.                 if (realsize >= memmap_pages) {  
  43.                         realsize -= memmap_pages;  
  44.                         if (memmap_pages)  
  45.                                 printk(KERN_DEBUG  
  46.                                        "  %s zone: %lu pages used for memmap\n",  
  47.                                        zone_names[j], memmap_pages);  
  48.                 } else /* 内存不够存放page结构体 */  
  49.                         printk(KERN_WARNING  
  50.                                 "  %s zone: %lu pages exceeds realsize %lu\n",  
  51.                                 zone_names[j], memmap_pages, realsize);  
  52.   
  53.                 /* 调整realsize的大小,即减去DMA保留页的大小 */  
  54.                 /* Account for reserved pages */  
  55.                 if (j == 0 && realsize > dma_reserve) {  
  56.                         realsize -= dma_reserve;  
  57.                         printk(KERN_DEBUG "  %s zone: %lu pages reserved\n",  
  58.                                         zone_names[0], dma_reserve);  
  59.                 }  
  60.   
  61.                 if (!is_highmem_idx(j))  
  62.                         nr_kernel_pages += realsize;  
  63.                 nr_all_pages += realsize;  
  64.   
  65.                 zone->spanned_pages = size; /* 设置zone->spanned_pages为包括洞的页框数 */  
  66.                 zone->present_pages = realsize; /* 设置zone->present+pages为不包括洞的页框数 */  
  67. #ifdef CONFIG_NUMA  
  68.                 zone->node = nid;       /* 设置zone中的节点标识符 */  
  69.                 /* 设置可回收页面比率 */  
  70.                 zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)  
  71.                                                 / 100;  
  72.                 /* 设置slab回收缓存页的比率 */  
  73.                 zone->min_slab_pages = (realsize * sysctl_min_slab_ratio) / 100;  
  74. #endif  
  75.                 zone->name = zone_names[j]; /* 设置zone的名称 */  
  76.   
  77.                 /* 初始化各种锁 */  
  78.                 spin_lock_init(&zone->lock);  
  79.                 spin_lock_init(&zone->lru_lock);  
  80.                 zone_seqlock_init(zone);  
  81.                 zone->zone_pgdat = pgdat; /* 设置管理区属于的节点对应的pg_data_t结构 */  
  82.   
  83.                 zone_pcp_init(zone); /* 初始化cpu的页面缓存 */  
  84.   
  85.                 /* 初始化lru相关成员 */  
  86.                 for_each_lru(l) {  
  87.                         INIT_LIST_HEAD(&zone->lru[l].list);  
  88.                         zone->reclaim_stat.nr_saved_scan[l] = 0;  
  89.                 }  
  90.                 zone->reclaim_stat.recent_rotated[0] = 0;  
  91.                 zone->reclaim_stat.recent_rotated[1] = 0;  
  92.                 zone->reclaim_stat.recent_scanned[0] = 0;  
  93.                 zone->reclaim_stat.recent_scanned[1] = 0;  
  94.   
  95.                 zap_zone_vm_stats(zone); /* 初始化zone->vm_stat为0 */  
  96.                 zone->flags = 0;  
  97.                 if (!size)  
  98.                         continue;  
  99.   
  100.                 set_pageblock_order(pageblock_default_order()); /* pageblock_default_order()返回9*/  
  101.                 setup_usemap(pgdat, zone, size);        /* 定义了CONFIG_SPARSEMEM该函数为空 */  
  102.   
  103.                 /* 设置pgdat->nr_zones和zone->zone_start_pfn成员  
  104.                  * 初始化zone->free_area成员 
  105.                  * 初始化zone->wait_table相关成员  
  106.                  */  
  107.                 ret = init_currently_empty_zone(zone, zone_start_pfn,  
  108.                                                 size, MEMMAP_EARLY);  
  109.                 BUG_ON(ret);  
  110.                 memmap_init(size, nid, j, zone_start_pfn); /* 初始化该zone对应的page结构 */  
  111.                 zone_start_pfn += size; /* 调整zone_start_pfn为下一个zone的起始页面 */  
  112.         }  
  113. }  



free_area_init_core函数调用memmap_init函数来初始化page结构:

[cpp] view plaincopy
  1. #define memmap_init(size, nid, zone, start_pfn) \  
  2.         memmap_init_zone((size), (nid), (zone), (start_pfn), MEMMAP_EARLY)  


[cpp] view plaincopy
  1. /* 
  2.  * Initially all pages are reserved - free ones are freed 
  3.  * up by free_all_bootmem() once the early boot process is 
  4.  * done. Non-atomic initialization, single-pass. 
  5.  */  
  6. /* 初始化该zone对应的page结构体 */  
  7. void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,  
  8.                 unsigned long start_pfn, enum memmap_context context)  
  9. {  
  10.       struct page *page;  
  11.       unsigned long end_pfn = start_pfn + size;  
  12.     unsigned long pfn;  
  13.         struct zone *z;  
  14.   
  15.         if (highest_memmap_pfn < end_pfn - 1)  /* 调整最高mem_map的页面数 */  
  16.                 highest_memmap_pfn = end_pfn - 1;  
  17.   
  18.         z = &NODE_DATA(nid)->node_zones[zone]; /* 取得zone的指针 */  
  19.         for (pfn = start_pfn; pfn < end_pfn; pfn++) {  
  20.                 /* 
  21.                  * There can be holes in boot-time mem_map[]s 
  22.                  * handed to this function.  They do not 
  23.                  * exist on hotplugged memory. 
  24.                  */  
  25.                 if (context == MEMMAP_EARLY) {  
  26.                         if (!early_pfn_valid(pfn))  
  27.                                 continue;  
  28.                         if (!early_pfn_in_nid(pfn, nid))  
  29.                                 continue;  
  30.                 }  
  31.                 page = pfn_to_page(pfn); /* 获得pfn对应的page结构, 此时page还未初始化 */  
  32.                 set_page_links(page, zone, nid, pfn);/* 设置page->flags中关于zone、node、section的标志位 */  
  33.   
  34.         mminit_verify_page_links(page, zone, nid, pfn);/* DEBUG用,无视之 */  
  35.                 init_page_count(page); /* 设置page->_count引用计数为1 */  
  36.                 reset_page_mapcount(page); /* 设置page->_mapcount为-1 */  
  37.                 SetPageReserved(page); /* 无此函数 */  
  38.   
  39.                 /* 
  40.                  * Mark the block movable so that blocks are reserved for 
  41.                  * movable at startup. This will force kernel allocations 
  42.                  * to reserve their blocks rather than leaking throughout 
  43.                  * the address space during boot when many long-lived 
  44.                  * kernel allocations are made. Later some blocks near 
  45.                  * the start are marked MIGRATE_RESERVE by 
  46.                  * setup_zone_migrate_reserve() 
  47.                  * 
  48.                  * bitmap is created for zone's valid pfn range. but memmap 
  49.                  * can be created for invalid pages (for alignment) 
  50.                  * check here not to call set_pageblock_migratetype() against 
  51.                  * pfn out of zone. 
  52.                  */  
  53.                 if ((z->zone_start_pfn <= pfn)  
  54.                     && (pfn < z->zone_start_pfn + z->spanned_pages)  
  55.                     && !(pfn & (pageblock_nr_pages - 1)))  
  56.                         set_pageblock_migratetype(page, MIGRATE_MOVABLE);  
  57.   
  58.                 INIT_LIST_HEAD(&page->lru); /* 初始化lru链表 */  
  59. #ifdef WANT_PAGE_VIRTUAL  
  60.                 /* The shift won't overflow because ZONE_NORMAL is below 4G. */  
  61.                 if (!is_highmem_idx(zone))  
  62.                         /* 设置page->virtual为页框的虚拟地址 */  
  63.                         set_page_address(page, __va(pfn << PAGE_SHIFT));  
  64. #endif  
  65.         }  
  66. }  


这样经过paging_init函数,pg_data_t、zone、page等结构完成了初始化。


0 0
原创粉丝点击