手机版
你好,游客 登录 注册 搜索
背景:
阅读新闻

Linux内存管理之伙伴系统(内存分配)

[日期:2012-01-09] 来源:Linux社区  作者:bullbat [字体: ]
一、Linux伙伴系统分配器

伙伴系统分配器大体上分为两类。__get_free_pages()类函数返回分配的第一个页面的线性地址;alloc_pages()类函数返回页面描述符地址。不管以哪种函数进行分配,最终会调用alloc_pages()进行分配页面。

为清楚了解其分配制度,先给个伙伴系统数据的存储框图

也就是每个order对应一个free_area结构,free_area以不同的类型以链表的方式存储这些内存块。

二、主分配函数

下面我们来看这个函数(在UMA模式下)

  1. #define alloc_pages(gfp_mask, order) \   
  2.         alloc_pages_node(numa_node_id(), gfp_mask, order)  
  3.    
 
  1. static inline struct page *alloc_pages_node(int nid, gfp_t gfp_mask,  
  2.                         unsigned int order)  
  3. {  
  4.     /* Unknown node is current node */  
  5.     if (nid < 0)  
  6.         nid = numa_node_id();  
  7.   
  8.     return __alloc_pages(gfp_mask, order, node_zonelist(nid, gfp_mask));  
  9. }  
 
  1. static inline struct page *  
  2. __alloc_pages(gfp_t gfp_mask, unsigned int order,  
  3.         struct zonelist *zonelist)  
  4. {  
  5.     return __alloc_pages_nodemask(gfp_mask, order, zonelist, NULL);  
  6. }  

上层分配函数__alloc_pages_nodemask()

  1. /* 
  2.  * This is the 'heart' of the zoned buddy allocator. 
  3.  */  
  4.  /*上层分配器运用了各种方式进行*/  
  5. struct page *  
  6. __alloc_pages_nodemask(gfp_t gfp_mask, unsigned int order,  
  7.             struct zonelist *zonelist, nodemask_t *nodemask)  
  8. {  
  9.     enum zone_type high_zoneidx = gfp_zone(gfp_mask);  
  10.     struct zone *preferred_zone;  
  11.     struct page *page;  
  12.       
  13.     /* Convert GFP flags to their corresponding migrate type */  
  14.     int migratetype = allocflags_to_migratetype(gfp_mask);  
  15.   
  16.     gfp_mask &= gfp_allowed_mask;  
  17.     /*调试用*/  
  18.     lockdep_trace_alloc(gfp_mask);  
  19.     /*如果__GFP_WAIT标志设置了,需要等待和重新调度*/  
  20.     might_sleep_if(gfp_mask & __GFP_WAIT);  
  21.     /*没有设置对应的宏*/  
  22.     if (should_fail_alloc_page(gfp_mask, order))  
  23.         return NULL;  
  24.   
  25.     /* 
  26.      * Check the zones suitable for the gfp_mask contain at least one 
  27.      * valid zone. It's possible to have an empty zonelist as a result 
  28.      * of GFP_THISNODE and a memoryless node 
  29.      */  
  30.     if (unlikely(!zonelist->_zonerefs->zone))  
  31.         return NULL;  
  32.   
  33.     /* The preferred zone is used for statistics later */  
  34.     /* 英文注释所说*/  
  35.     first_zones_zonelist(zonelist, high_zoneidx, nodemask, &preferred_zone);  
  36.     if (!preferred_zone)  
  37.         return NULL;  
  38.   
  39.     /* First allocation attempt */  
  40.     /*从pcp和伙伴系统中正常的分配内存空间*/  
  41.     page = get_page_from_freelist(gfp_mask|__GFP_HARDWALL, nodemask, order,  
  42.             zonelist, high_zoneidx, ALLOC_WMARK_LOW|ALLOC_CPUSET,  
  43.             preferred_zone, migratetype);  
  44.     if (unlikely(!page))/*如果上面没有分配到空间,调用下面函数慢速分配,允许等待和回收*/  
  45.         page = __alloc_pages_slowpath(gfp_mask, order,  
  46.                 zonelist, high_zoneidx, nodemask,  
  47.                 preferred_zone, migratetype);  
  48.     /*调试用*/  
  49.     trace_mm_page_alloc(page, order, gfp_mask, migratetype);  
  50.     return page;  
  51. }  

三、从pcp和伙伴系统中正常的分配内存空间

函数get_page_from_freelist()

  1. /* 
  2.  * get_page_from_freelist goes through the zonelist trying to allocate 
  3.  * a page. 
  4.  */  
  5. /*为分配制定内存空间,遍历每个zone*/  
  6. static struct page *  
  7. get_page_from_freelist(gfp_t gfp_mask, nodemask_t *nodemask, unsigned int order,  
  8.         struct zonelist *zonelist, int high_zoneidx, int alloc_flags,  
  9.         struct zone *preferred_zone, int migratetype)  
  10. {  
  11.     struct zoneref *z;  
  12.     struct page *page = NULL;  
  13.     int classzone_idx;  
  14.     struct zone *zone;  
  15.     nodemask_t *allowednodes = NULL;/* zonelist_cache approximation */  
  16.     int zlc_active = 0;     /* set if using zonelist_cache */  
  17.     int did_zlc_setup = 0;      /* just call zlc_setup() one time */  
  18.     /*zone对应的下标*/  
  19.     classzone_idx = zone_idx(preferred_zone);  
  20. zonelist_scan:  
  21.     /* 
  22.      * Scan zonelist, looking for a zone with enough free. 
  23.      * See also cpuset_zone_allowed() comment in kernel/cpuset.c. 
  24.      */  
  25.      /*遍历每个zone,进行分配*/  
  26.     for_each_zone_zonelist_nodemask(zone, z, zonelist,  
  27.         /*在UMA模式下不成立*/              high_zoneidx, nodemask) {  
  28.         if (NUMA_BUILD && zlc_active &&  
  29.             !zlc_zone_worth_trying(zonelist, z, allowednodes))  
  30.                 continue;  
  31.         if ((alloc_flags & ALLOC_CPUSET) &&  
  32.             !cpuset_zone_allowed_softwall(zone, gfp_mask))  
  33.                 goto try_next_zone;  
  34.   
  35.         BUILD_BUG_ON(ALLOC_NO_WATERMARKS < NR_WMARK);  
  36.         /*需要关注水位*/  
  37.         if (!(alloc_flags & ALLOC_NO_WATERMARKS)) {  
  38.             unsigned long mark;  
  39.             int ret;  
  40.             /*从flags中取的mark*/  
  41.             mark = zone->watermark[alloc_flags & ALLOC_WMARK_MASK];  
  42.             /*如果水位正常,从本zone中分配*/  
  43.             if (zone_watermark_ok(zone, order, mark,  
  44.                     classzone_idx, alloc_flags))  
  45.                 goto try_this_zone;  
  46.   
  47.             if (zone_reclaim_mode == 0)/*如果上面检查的水位低于正常值,且没有设置页面回收值*/  
  48.                 goto this_zone_full;  
  49.             /*在UMA模式下下面函数直接返回0*/  
  50.             ret = zone_reclaim(zone, gfp_mask, order);  
  51.             switch (ret) {  
  52.             case ZONE_RECLAIM_NOSCAN:  
  53.                 /* did not scan */  
  54.                 goto try_next_zone;  
  55.             case ZONE_RECLAIM_FULL:  
  56.                 /* scanned but unreclaimable */  
  57.                 goto this_zone_full;  
  58.             default:  
  59.                 /* did we reclaim enough */  
  60.                 if (!zone_watermark_ok(zone, order, mark,  
  61.                         classzone_idx, alloc_flags))  
  62.                     goto this_zone_full;  
  63.             }  
  64.         }  
  65.   
  66. try_this_zone:/*本zone正常水位*/  
  67.     /*先从pcp中分配,然后不行的话再从伙伴系统中分配*/  
  68.         page = buffered_rmqueue(preferred_zone, zone, order,  
  69.                         gfp_mask, migratetype);  
  70.         if (page)  
  71.             break;  
  72. this_zone_full:  
  73.         if (NUMA_BUILD)/*UMA模式为0*/  
  74.             zlc_mark_zone_full(zonelist, z);  
  75. try_next_zone:  
  76.         if (NUMA_BUILD && !did_zlc_setup && nr_online_nodes > 1) {  
  77.             /* 
  78.              * we do zlc_setup after the first zone is tried but only 
  79.              * if there are multiple nodes make it worthwhile 
  80.              */  
  81.             allowednodes = zlc_setup(zonelist, alloc_flags);  
  82.             zlc_active = 1;  
  83.             did_zlc_setup = 1;  
  84.         }  
  85.     }  
  86.   
  87.     if (unlikely(NUMA_BUILD && page == NULL && zlc_active)) {  
  88.         /* Disable zlc cache for second zonelist scan */  
  89.         zlc_active = 0;  
  90.         goto zonelist_scan;  
  91.     }  
  92.     return page;/*返回页面*/  
  93. }  

主分配函数

  1. /* 
  2.  * Really, prep_compound_page() should be called from __rmqueue_bulk().  But 
  3.  * we cheat by calling it from here, in the order > 0 path.  Saves a branch 
  4.  * or two. 
  5.  */  
  6.  /*先考虑从pcp中分配空间,当order大于0时再考虑从伙伴系统中分配*/  
  7. static inline  
  8. struct page *buffered_rmqueue(struct zone *preferred_zone,  
  9.             struct zone *zone, int order, gfp_t gfp_flags,  
  10.             int migratetype)  
  11. {  
  12.     unsigned long flags;  
  13.     struct page *page;  
  14.     int cold = !!(gfp_flags & __GFP_COLD);/*如果分配参数指定了__GFP_COLD标志,则设置cold标志*/  
  15.     int cpu;  
  16.   
  17. again:  
  18.     cpu  = get_cpu();  
  19.     if (likely(order == 0)) {/*分配一个页面时,使用pcp*/  
  20.         struct per_cpu_pages *pcp;  
  21.         struct list_head *list;  
  22.         /*找到zone对应的pcp*/  
  23.         pcp = &zone_pcp(zone, cpu)->pcp;  
  24.         list = &pcp->lists[migratetype];/*pcp中对应类型的list*/  
  25.           
  26.         /* 这里需要关中断,因为内存回收过程可能发送核间中断,强制每个核从每CPU 
  27.         缓存中释放页面。而且中断处理函数也会分配单页。 */  
  28.         local_irq_save(flags);  
  29.         if (list_empty(list)) {/*如果pcp中没有页面,需要补充*/  
  30.             /*从伙伴系统中获得batch个页面 
  31.             batch为一次分配的页面数*/  
  32.             pcp->count += rmqueue_bulk(zone, 0,  
  33.                     pcp->batch, list,  
  34.                     migratetype, cold);  
  35.             /*如果链表仍然为空,申请失败返回*/  
  36.             if (unlikely(list_empty(list)))  
  37.                 goto failed;  
  38.         }  
  39.         /* 如果分配的页面不需要考虑硬件缓存(注意不是每CPU页面缓存) 
  40.         ,则取出链表的最后一个节点返回给上层*/  
  41.         if (cold)  
  42.             page = list_entry(list->prev, struct page, lru);  
  43.         else/* 如果要考虑硬件缓存,则取出链表的第一个页面,这个页面是最近刚释放到每CPU 
  44.             缓存的,缓存热度更高 */  
  45.             page = list_entry(list->next, struct page, lru);  
  46.   
  47.         list_del(&page->lru);/*从pcp中脱离*/  
  48.         pcp->count--;/*pcp计数减一*/  
  49.     }   
  50.     else {/*当order为大于1时,不从pcp中分配,直接考虑从伙伴系统中分配*/  
  51.         if (unlikely(gfp_flags & __GFP_NOFAIL)) {  
  52.             /* 
  53.              * __GFP_NOFAIL is not to be used in new code. 
  54.              * 
  55.              * All __GFP_NOFAIL callers should be fixed so that they 
  56.              * properly detect and handle allocation failures. 
  57.              * 
  58.              * We most definitely don't want callers attempting to 
  59.              * allocate greater than order-1 page units with 
  60.              * __GFP_NOFAIL. 
  61.              */  
  62.             WARN_ON_ONCE(order > 1);  
  63.         }  
  64.         /* 关中断,并获得管理区的锁*/  
  65.         spin_lock_irqsave(&zone->lock, flags);  
  66.         /*从伙伴系统中相应类型的相应链表中分配空间*/  
  67.         page = __rmqueue(zone, order, migratetype);  
  68.         /* 已经分配了1 << order个页面,这里进行管理区空闲页面统计计数*/  
  69.         __mod_zone_page_state(zone, NR_FREE_PAGES, -(1 << order));  
  70.         spin_unlock(&zone->lock);/* 这里仅仅打开自旋锁,待后面统计计数设置完毕后再开中断*/  
  71.         if (!page)  
  72.             goto failed;  
  73.     }  
  74.     /*事件统计计数,调试*/  
  75.     __count_zone_vm_events(PGALLOC, zone, 1 << order);  
  76.     zone_statistics(preferred_zone, zone);  
  77.     local_irq_restore(flags);/*恢复中断*/  
  78.     put_cpu();  
  79.   
  80.     VM_BUG_ON(bad_range(zone, page));  
  81.       
  82.      /* 这里进行安全性检查,并进行一些善后工作。 
  83.       如果页面标志破坏,返回的页面出现了问题,则返回试图分配其他页面*/  
  84.     if (prep_new_page(page, order, gfp_flags))  
  85.         goto again;  
  86.     return page;  
  87.   
  88. failed:  
  89.     local_irq_restore(flags);  
  90.     put_cpu();  
  91.     return NULL;  
  92. }  
linux
相关资讯       Linux内存 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款