linux內存夥伴算法(初始化內存域和數據結構)

體系結構相關代碼須要在啓動期間創建如下信息:node

1.系統中各個內存域的頁幀邊界,保存在max_zone_pfn中跨域

2.個結點頁幀的分配狀況,保存在全局變量early_node_map中。數組

從內核版本2.6.10開始提供一個通用的框架,用於將上述信息轉換爲夥伴系統預期的 結點和內存域數據結構。在這之前,各個體系結構必須自行創建相關結構。如今,體系結構相關代碼只須要創建前述的簡單結構,將繁重的工做留給 free_area_init_nodes便可。圖1給出了該過程概述,圖2給出了free_area_init_nodes的代碼流程圖。緩存

                                                                                        圖1:free_area_init_nodes過程概述數據結構

                                          圖2:free_area_init_nodes代碼流程圖app

free_area_init_nodes的源代碼的詳細分析以下:框架

  1. void __init free_area_init_nodes(unsigned long *max_zone_pfn)  
  2. {  
  3.     unsigned long nid;  
  4.     enum zone_type i;  
  5.   
  6.     /* Sort early_node_map as initialisation assumes it is sorted */  
  7.     sort_node_map();//排序使得後續的任務稍微容易些,排序自己並不特別複雜  
  8.   
  9.     /* Record where the zone boundaries are */  
  10.     memset(arch_zone_lowest_possible_pfn, 0,  
  11.                 sizeof(arch_zone_lowest_possible_pfn));//全局數組arch_zone_lowest_possible_pfn用來存儲各個內存域可以使用的最低內存頁幀編號  
  12.     memset(arch_zone_highest_possible_pfn, 0,  
  13.             sizeof(arch_zone_highest_possible_pfn));//全局數組arch_zone_highest_possible_pfn用來存儲各個內存域可以使用的最高內存頁幀編號  
  14.     arch_zone_lowest_possible_pfn[0] = find_min_pfn_with_active_regions();//輔助函數find_min_pfn_with_active_regions用於找到註冊的最低內存域中可用的編號最小的頁幀  
  15.   
  16.     arch_zone_highest_possible_pfn[0] = max_zone_pfn[0];//max_zone_pfn記錄了各個內存域包含的最大頁幀號  
  17.     for (i = 1; i < MAX_NR_ZONES; i++) {//依次遍歷,肯定各個內存域的邊界  
  18.         if (i == ZONE_MOVABLE)//因爲ZONE_MOVABLE是一個虛擬內存域,不與真正的硬件內存域關聯,該內存域的邊界老是設置爲0,如後面的代碼所示  
  19.             continue;  
  20.         arch_zone_lowest_possible_pfn[i] =  
  21.             arch_zone_highest_possible_pfn[i-1];//第n個內存域的最小頁幀,即前一個(第n-1個)內存域的最大頁幀  
  22.         arch_zone_highest_possible_pfn[i] =  
  23.             max(max_zone_pfn[i], arch_zone_lowest_possible_pfn[i]);//不出意外,當前內存域的最大頁幀由max_zone_pfn給出  
  24.     }  
  25.     arch_zone_lowest_possible_pfn[ZONE_MOVABLE] = 0;  
  26.     arch_zone_highest_possible_pfn[ZONE_MOVABLE] = 0;  
  27.   
  28.     /* Find the PFNs that ZONE_MOVABLE begins at in each node */  
  29.     memset(zone_movable_pfn, 0, sizeof(zone_movable_pfn));  
  30.     find_zone_movable_pfns_for_nodes(zone_movable_pfn);//用於計算進入ZONE_MOVABLE的內存數量,詳細分析見下文  
  31.   
  32.     /* Print out the zone ranges */  
  33.     printk("Zone PFN ranges:\n");  
  34.     for (i = 0; i < MAX_NR_ZONES; i++) {//將各個內存域的最大、最小頁幀號顯示出來  
  35.         if (i == ZONE_MOVABLE)  
  36.             continue;  
  37.         printk("  %-8s %8lu -> %8lu\n",  
  38.                 zone_names[i],  
  39.                 arch_zone_lowest_possible_pfn[i],  
  40.                 arch_zone_highest_possible_pfn[i]);  
  41.     }  
  42.   
  43.     /* Print out the PFNs ZONE_MOVABLE begins at in each node */  
  44.     printk("Movable zone start PFN for each node\n");  
  45.     for (i = 0; i < MAX_NUMNODES; i++) {  
  46.         if (zone_movable_pfn[i])//對每一個結點來講,zone_movable_pfn[node_id]表示ZONE_MOVABLE在movable_zone內存域中所取得內存的起始地址。內核確保這些頁將用於知足符合ZONE_MOVABLE職責的內存分配  
  47.             printk("  Node %d: %lu\n", i, zone_movable_pfn[i]);  
  48.     }  
  49.   
  50.     /* Print out the early_node_map[] */  
  51.     printk("early_node_map[%d] active PFN ranges\n", nr_nodemap_entries);  
  52.     for (i = 0; i < nr_nodemap_entries; i++)//顯示各個內存域的分配狀況  
  53.         printk("  %3d: %8lu -> %8lu\n", early_node_map[i].nid,  
  54.                         early_node_map[i].start_pfn,  
  55.                         early_node_map[i].end_pfn);  
  56.   
  57.     /* Initialise every node */  
  58.     setup_nr_node_ids();  
  59.     for_each_online_node(nid) {//代碼遍歷全部的活動結點,並分別對各個結點調用free_area_init_node創建數據結構,該函數須要結點第一個可用的頁幀做爲一個參數,而find_min_pfn_for_node則從early_node_map數組提取該信息  
  60.         pg_data_t *pgdat = NODE_DATA(nid);  
  61.         free_area_init_node(nid, pgdat, NULL,  
  62.                 find_min_pfn_for_node(nid), NULL);  
  63.   
  64.         /* Any memory on that node */  
  65.         if (pgdat->node_present_pages)// 根據node_present_pages字段判斷結點具備內存,則在結點位圖中設置N_HIGH_MEMORY標誌,該標誌只表示結點上存在普通或高端 內存,所以check_for_regular_memory進一步檢查低於ZONE_HIGHMEM的內存域中是否有內存,並據此在結點位圖中相應地設 置N_NORMAL_MEMORY  
  66.             node_set_state(nid, N_HIGH_MEMORY);  
  67.         check_for_regular_memory(pgdat);  
  68.     }  
  69. }  

free_area_init_node源代碼詳細分析:函數

  1. void __meminit free_area_init_node(int nid, struct pglist_data *pgdat,  
  2.         unsigned long *zones_size, unsigned long node_start_pfn,  
  3.         unsigned long *zholes_size)  
  4. {  
  5.     pgdat->node_id = nid;  
  6.     pgdat->node_start_pfn = node_start_pfn;  
  7.     calculate_node_totalpages(pgdat, zones_size, zholes_size);//首先累計各個內存域的頁數,計算結點中頁的總數。對連續內存模型而言,這能夠經過zone_sizes_init完成,但calculate_node_totalpages還考慮了內存空洞  
  8.   
  9.     alloc_node_mem_map(pgdat);//分配了該節點的頁面描述符數組[pgdat->node_mem_map數組的內存分配]   
  10.   
  11.     free_area_init_core(pgdat, zones_size, zholes_size);//對該節點的每一個區[DMA,NORMAL,HIGH]的的結構進行初始化  
  12. }  
calculate_node_totalpages源代碼詳細分析:
  1. static void __meminit calculate_node_totalpages(struct pglist_data *pgdat,  
  2.         unsigned long *zones_size, unsigned long *zholes_size)  
  3. {  
  4.     unsigned long realtotalpages, totalpages = 0;  
  5.     enum zone_type i;  
  6.   
  7.     for (i = 0; i < MAX_NR_ZONES; i++)  
  8.         totalpages += zone_spanned_pages_in_node(pgdat->node_id, i,  
  9.                                 zones_size);//累計計算各個內存域包含空洞的內存總頁數  
  10.     pgdat->node_spanned_pages = totalpages;  
  11.   
  12.     realtotalpages = totalpages;  
  13.     for (i = 0; i < MAX_NR_ZONES; i++)  
  14.         realtotalpages -=  
  15.             zone_absent_pages_in_node(pgdat->node_id, i,  
  16.                                 zholes_size)//;以包含空洞的內存總頁數累計減去各個內存域中空洞的數量,就能夠得出實際可用的內存頁數  
  17.     pgdat->node_present_pages = realtotalpages;  
  18.     printk(KERN_DEBUG "On node %d totalpages: %lu\n", pgdat->node_id,  
  19.                             realtotalpages);  
  20. }  
alloc_node_mem_map源代碼詳細分析:
  1. static void __init_refok alloc_node_mem_map(struct pglist_data *pgdat)  
  2. {  
  3.     /* Skip empty nodes */  
  4.     if (!pgdat->node_spanned_pages)//若是內存結點沒有沒存頁,直接返回  
  5.         return;  
  6.   
  7. #ifdef CONFIG_FLAT_NODE_MEM_MAP  
  8.     /* ia64 gets its own node_mem_map, before this, without bootmem */  
  9.     if (!pgdat->node_mem_map) {//若是尚未爲結點分配mem_map,則須要爲結點分配mem_map  
  10.         unsigned long size, start, end;  
  11.         struct page *map;  
  12.   
  13.         /* 
  14.          * The zone's endpoints aren't required to be MAX_ORDER 
  15.          * aligned but the node_mem_map endpoints must be in order 
  16.          * for the buddy allocator to function correctly. 
  17.          */  
  18.         start = pgdat->node_start_pfn & ~(MAX_ORDER_NR_PAGES - 1);//肯定起點,以MAX_ORDER_NR_PAGES的大小對齊  
  19.         end = pgdat->node_start_pfn + pgdat->node_spanned_pages;//計算結束點  
  20.         end = ALIGN(end, MAX_ORDER_NR_PAGES);//以MAX_ORDER_NR_PAGES對齊,與上面的功能一致,將內存映射對齊到夥伴系統的最大分配階  
  21.         size =  (end - start) * sizeof(struct page);//計算所需內存的大小  
  22.         map = alloc_remap(pgdat->node_id, size);//爲內存映射分配內存  
  23.         if (!map)//若是分配不成功,則使用普通的自舉內存分配器進行分配  
  24.             map = alloc_bootmem_node(pgdat, size);  
  25.         pgdat->node_mem_map = map + (pgdat->node_start_pfn - start);  
  26.     }  
  27. #ifndef CONFIG_NEED_MULTIPLE_NODES  
  28.     /* 
  29.      * With no DISCONTIG, the global mem_map is just set as node 0's 
  30.      */  
  31.     if (pgdat == NODE_DATA(0)) {  
  32.         mem_map = NODE_DATA(0)->node_mem_map;  
  33. #ifdef CONFIG_ARCH_POPULATES_NODE_MAP  
  34.         if (page_to_pfn(mem_map) != pgdat->node_start_pfn)  
  35.             mem_map -= (pgdat->node_start_pfn - ARCH_PFN_OFFSET);  
  36. #endif /* CONFIG_ARCH_POPULATES_NODE_MAP */  
  37.     }  
  38. #endif  
  39. #endif /* CONFIG_FLAT_NODE_MEM_MAP */  
  40. }  

free_area_init_core源代碼詳細分析:ui

  1. static void __meminit free_area_init_core(struct pglist_data *pgdat,  
  2.         unsigned long *zones_size, unsigned long *zholes_size)  
  3. {  
  4.     enum zone_type j;  
  5.     int nid = pgdat->node_id;  
  6.     unsigned long zone_start_pfn = pgdat->node_start_pfn;  
  7.     int ret;  
  8.   
  9.     pgdat_resize_init(pgdat);  
  10.     pgdat->nr_zones = 0;  
  11.     init_waitqueue_head(&pgdat->kswapd_wait);  
  12.     pgdat->kswapd_max_order = 0;  
  13.       
  14.     for (j = 0; j < MAX_NR_ZONES; j++) {  
  15.         struct zone *zone = pgdat->node_zones + j;  
  16.         unsigned long size, realsize, memmap_pages;  
  17.   
  18.         size = zone_spanned_pages_in_node(nid, j, zones_size);//內存域跨域的頁數  
  19.         realsize = size - zone_absent_pages_in_node(nid, j,  
  20.                                 zholes_size);//內存域的可用長度,可經過跨域的頁數減去空洞覆蓋的頁數而獲得  
  21.   
  22.         /* 
  23.          * Adjust realsize so that it accounts for how much memory 
  24.          * is used by this zone for memmap. This affects the watermark 
  25.          * and per-cpu initialisations 
  26.          */  
  27.         memmap_pages = (size * sizeof(struct page)) >> PAGE_SHIFT;//用於內存映射須要的頁數  
  28.         if (realsize >= memmap_pages) {若是內存域的可用長度大於用於內存映射須要的頁數  
  29.             realsize -= memmap_pages;//則將須要映射的頁數分配出去  
  30.             printk(KERN_DEBUG  
  31.                 "  %s zone: %lu pages used for memmap\n",  
  32.                 zone_names[j], memmap_pages);  
  33.         } else//不然,顯示警告信息,可用內存不足  
  34.             printk(KERN_WARNING  
  35.                 "  %s zone: %lu pages exceeds realsize %lu\n",  
  36.                 zone_names[j], memmap_pages, realsize);  
  37.   
  38.         /* Account for reserved pages */  
  39.         if (j == 0 && realsize > dma_reserve) {  
  40.             realsize -= dma_reserve;  
  41.             printk(KERN_DEBUG "  %s zone: %lu pages reserved\n",  
  42.                     zone_names[0], dma_reserve);  
  43.         }//除去用於保留的內存頁  
  44.   
  45.         if (!is_highmem_idx(j))  
  46.             nr_kernel_pages += realsize;//nr_kernel_pages表示不包含高端內存的系統內存共有的內存頁面數,用於統計全部一致映射的頁  
  47.         nr_all_pages += realsize;  
  48.   
  49.         zone->spanned_pages = size;//跨域的內存頁  
  50.         zone->present_pages = realsize;//通過一系列初始化以後,還可以使用的內存頁  
  51. #ifdef CONFIG_NUMA  
  52.         zone->node = nid;  
  53.         zone->min_unmapped_pages = (realsize*sysctl_min_unmapped_ratio)/ 100;//這句話不理解,請指教  
  54.         zone->min_slab_pages = (realsize * sysctl_min_slab_ratio) / 100;//這句話不理解,請指教  
  55. #endif  
  56.         zone->name = zone_names[j];  
  57.         spin_lock_init(&zone->lock);//關於鎖機制,本身尚未學到,後面會詳細介紹鎖機制  
  58.         spin_lock_init(&zone->lru_lock);  
  59.         zone_seqlock_init(zone);  
  60.         zone->zone_pgdat = pgdat;  
  61.   
  62.         zone->prev_priority = DEF_PRIORITY;  
  63.   
  64.         zone_pcp_init(zone);//初始化該內存域的per_cpu緩存  
  65.         INIT_LIST_HEAD(&zone->active_list);  
  66.         INIT_LIST_HEAD(&zone->inactive_list);  
  67.         zone->nr_scan_active = 0;  
  68.         zone->nr_scan_inactive = 0;  
  69.         zap_zone_vm_stats(zone);  
  70.         zone->flags = 0;  
  71.         if (!size)  
  72.             continue;  
  73.   
  74.         set_pageblock_order(pageblock_default_order());  
  75.         setup_usemap(pgdat, zone, size);  
  76.         ret = init_currently_empty_zone(zone, zone_start_pfn,  
  77.                         size, MEMMAP_EARLY);//init_currently_empty_zone用於初始化free_area列表,並將屬於該內存域的全部page實例都設置爲初始默認值  
  78.         BUG_ON(ret);  
  79.         zone_start_pfn += size;  
  80.     }  
  81. }  
check_for_regular_memory源代碼詳細分析:
  1. static void check_for_regular_memory(pg_data_t *pgdat)  
  2. {  
  3. #ifdef CONFIG_HIGHMEM  
  4.     enum zone_type zone_type;  
  5.   
  6.     for (zone_type = 0; zone_type <= ZONE_NORMAL; zone_type++) {//進一步檢查低於ZONE_HIGHMEM的內存域中是否有內存  
  7.   
  8.         struct zone *zone = &pgdat->node_zones[zone_type];  
  9.         if (zone->present_pages)  
  10.             node_set_state(zone_to_nid(zone), N_NORMAL_MEMORY);//並根據上面的檢查在結點位圖中相應地設置N_NORMAL_MEMORY  
  11.   
  12.     }  
  13. #endif  this

  14. }
相關文章
相關標籤/搜索