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

本文详细介绍了Linux内核中的伙伴系统内存分配器,包括两大类分配函数、主分配流程、从pcp和伙伴系统中分配内存的过程,以及慢速分配策略。在分配过程中,涉及到了zone数据结构、内存水线、内存回收等多个关键点,揭示了Linux内存管理的复杂性和高效性。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

 一、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 
  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 
  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.            
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值