linux内核工程师 3.03节 伙伴系统分配器 - buffered_rmqueue

在__alloc_pages过程中,操作分为两个部分,第一部分是找到适当的内存域,接下来的一部分就是按照伙伴系统的分配方式,从free_lists中移除这些页。

buffered_rmqueue就是用来完成第二部分工作的。

1. 首先判断要分配的页面数是否为 1,如果为1 的情况下,那么并不需要从buddy系统获取,因为per-CPU的页缓存提供了更快的分配和释放机制。per-CPU cache提供了两个链表,一个是cold page链表,另外一个是hot page链表。参见 cold page和hot page介绍。从hot-cold 链表获取page时要考虑迁移类型。

[html]  view plain  copy
  1. /* Find a page of the appropriate migrate type */  
  2. list_for_each_entry(page, &pcp->list, lru)  
  3.         if (page_private(page) == migratetype)  
  4.                 break;  

如果per-CPU页缓存无法满足分配,那么调用rmqueue_bulk从buddy进行bulk分配。

2. 如果是order大于0的情况,使用__rmqueue辅助函数进程分配。

[html]  view plain  copy
  1.  817 /*  
  2.  818  * Do the hard work of removing an element from the buddy allocator.  
  3.  819  * Call me with the zone->lock already held.  
  4.  820  */  
  5.  821 static struct page *__rmqueue(struct zone *zone, unsigned int order,  
  6.  822                                                 int migratetype)  
  7.  823 {  
  8.  824         struct page *page;  
  9.  825   
  10.  826         page = __rmqueue_smallest(zone, order, migratetype);  
  11.  827   
  12.  828         if (unlikely(!page))  
  13.  829                 page = __rmqueue_fallback(zone, order, migratetype);  
  14.  830   
  15.  831         return page;  
  16.  832 }  

826 __rmqueue_smallest扫描@migratetype指定的类型列表,找到合适的连续内存快。

829 如果__rmqueue_smallest失败,那么就调用__rmqueue_fallback尝试从其他类型的链表分配。我们应该还记得static int fallbacks[MIRGRATE_TYPES][MIGRATE_TYPES-1] 这个fallback数据


辅助函数__rmqueue_smallest

[html]  view plain  copy
  1.  643 /*  
  2.  644  * Go through the free lists for the given migratetype and remove  
  3.  645  * the smallest available page from the freelists  
  4.  646  */  
  5.  647 static struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,  
  6.  648                                                 int migratetype)  
  7.  649 {  
  8.  650         unsigned int current_order;  
  9.  651         struct free_area * area;  
  10.  652         struct page *page;  
  11.  653   
  12.  654         /* Find a page of the appropriate size in the preferred list */  
  13.  655         for (current_order = order; current_order < MAX_ORDER; ++current_order) {  
  14.  656                 area = &(zone->free_area[current_order]);  
  15.  657                 if (list_empty(&area->free_list[migratetype]))  
  16.  658                         continue;  
  17.  659   
  18.  660                 page = list_entry(area->free_list[migratetype].next,  
  19.  661                                                         struct page, lru);  
  20.  662                 list_del(&page->lru);  
  21.  663                 rmv_page_order(page);  
  22.  664                 area->nr_free--;  
  23.  665                 __mod_zone_page_state(zone, NR_FREE_PAGES, - (1UL << order));  
  24.  666                 expand(zone, page, order, current_order, area, migratetype);  
  25.  667                 return page;  
  26.  668         }  
  27.  669   
  28.  670         return NULL;  
  29.  671 }  

这个函数并不复杂,首先从满足条件的order开始,找到满足条件的空闲页,函数名字中的smallest表示:获得的空闲页表项应该是满足条件空闲表项中最小的。

657 首先这个项要满足迁移类型,buddy系统把不同迁移类型的内存块放在不同的链表中,迁移类型包括 MOVABLE UNMOVABLE和RECLAIMABLE。

660 ~ 665 把内存块从free_list中删除,做一些善后处理。

666 如果分配的内存块长度小于所选择的内存块长度,那是因为没有更小的适当内存块可用,因而会从较高order的分配内存块,此时系统需要把选择的内存块分割成小块,只保留申请大小的内存块,而把后面的内存块返回给buddy 系统。expand函数正是完成此功能的。


辅助函数__rmqueue_fallback

如果__rmqueue_smallest失败,说明在这个内存区的特定迁移类型链表上没有内存块满足条件,__rmqueue_fallback会尝试根据static int fallbacks[MIRGRATE_TYPES][MIGRATE_TYPES-1],从其他迁移类型链表分配。

这个和zonelists有点类似,前者是在给定的zone上进行fallback,而后者是在不同的zone上fallback.

[html]  view plain  copy
  1.  751 /* Remove an element from the buddy allocator from the fallback list */  
  2.  752 static struct page *__rmqueue_fallback(struct zone *zone, int order,  
  3.  753                                                 int start_migratetype)  
  4.  754 {  
  5.  755         struct free_area * area;  
  6.  756         int current_order;  
  7.  757         struct page *page;  
  8.  758         int migratetype, i;  
  9.  759   
  10.  760         /* Find the largest possible block of pages in the other list */  
  11.  761         for (current_order = MAX_ORDER-1; current_order >= order;  
  12.  762                                                 --current_order) {  
  13.  763                 for (i = 0; i < MIGRATE_TYPES - 1; i++) {  
  14.  764                         migratetype = fallbacks[start_migratetype][i];  
  15.  765   
  16.  766                         /* MIGRATE_RESERVE handled later if necessary */  
  17.  767                         if (migratetype == MIGRATE_RESERVE)  
  18.  768                                 continue;  
  19.  769   
  20.  770                         area = &(zone->free_area[current_order]);  
  21.  771                         if (list_empty(&area->free_list[migratetype]))  
  22.  772                                 continue;  
  23.  773   
  24.  774                         page = list_entry(area->free_list[migratetype].next,  
  25.  775                                         struct page, lru);  
  26.  776                         area->nr_free--;  
  27.  777   
  28.  778                         /*  
  29.  779                          * If breaking a large block of pages, move all free  
  30.  780                          * pages to the preferred allocation list. If falling  
  31.  781                          * back for a reclaimable kernel allocation, be more  
  32.  782                          * agressive about taking ownership of free pages  
  33.  783                          */  
  34.  784                         if (unlikely(current_order >= (pageblock_order >> 1)) ||  
  35.  785                                         start_migratetype == MIGRATE_RECLAIMABLE) {  
  36.  786                                 unsigned long pages;  
  37.  787                                 pages = move_freepages_block(zone, page,  
  38.  788                                                                 start_migratetype);  
  39.  789   
  40.  790                                 /* Claim the whole block if over half of it is free */  
  41.  791                                 if (pages >= (1 << (pageblock_order-1)))  
  42.  792                                         set_pageblock_migratetype(page,  
  43.  793                                                                 start_migratetype);  
  44.  794   
  45.  795                                 migratetype = start_migratetype;  
  46.  796                         }  
  47.  797   
  48.  798                         /* Remove the page from the freelists */  
  49.  799                         list_del(&page->lru);  
  50.  800                         rmv_page_order(page);  
  51.  801                         __mod_zone_page_state(zone, NR_FREE_PAGES,  
  52.  802                                                         -(1UL << order));  
  53.  803   
  54.  804                         if (current_order == pageblock_order)  
  55.  805                                 set_pageblock_migratetype(page,  
  56.  806                                                         start_migratetype);  
  57.  807   
  58.  808                         expand(zone, page, order, current_order, area, migratetype);  
  59.  809                         return page;  
  60.  810                 }  
  61.  811         }  
  62.  812   
  63.  813         /* Use MIGRATE_RESERVE rather than fail an allocation */  
  64.  814         return __rmqueue_smallest(zone, order, MIGRATE_RESERVE);  
  65.  815 }  
  66.   
  67. 和__rmqueue_smallest相反,函数从大到小进行遍历。隐含的策略是作者认为选择更大的块,更容易避免碎片,因为不同迁移类型的内存块会混合起来。   
767 并不会尝试使用紧急分配的块,因为我们认为为紧急分配预留的块更重要,所以万不得以才分配。其实把紧急预留内存放在fallback最后不久好了吗?

814 一切都失败后,从紧急分配的预留链表进行分配。


猜你喜欢

转载自blog.csdn.net/zjy900507/article/details/80681291