伙伴系统分配器 - 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时要考虑迁移类型。

                /* Find a page of the appropriate migrate type */
                list_for_each_entry(page, &pcp->list, lru)
                        if (page_private(page) == migratetype)
                                break;

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

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

 817 /*
 818  * Do the hard work of removing an element from the buddy allocator.
 819  * Call me with the zone->lock already held.
 820  */
 821 static struct page *__rmqueue(struct zone *zone, unsigned int order,
 822                                                 int migratetype)
 823 {
 824         struct page *page;
 825 
 826         page = __rmqueue_smallest(zone, order, migratetype);
 827 
 828         if (unlikely(!page))
 829                 page = __rmqueue_fallback(zone, order, migratetype);
 830 
 831         return page;
 832 }

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

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


辅助函数__rmqueue_smallest

 643 /*
 644  * Go through the free lists for the given migratetype and remove
 645  * the smallest available page from the freelists
 646  */
 647 static struct page *__rmqueue_smallest(struct zone *zone, unsigned int order,
 648                                                 int migratetype)
 649 {
 650         unsigned int current_order;
 651         struct free_area * area;
 652         struct page *page;
 653 
 654         /* Find a page of the appropriate size in the preferred list */
 655         for (current_order = order; current_order < MAX_ORDER; ++current_order) {
 656                 area = &(zone->free_area[current_order]);
 657                 if (list_empty(&area->free_list[migratetype]))
 658                         continue;
 659 
 660                 page = list_entry(area->free_list[migratetype].next,
 661                                                         struct page, lru);
 662                 list_del(&page->lru);
 663                 rmv_page_order(page);
 664                 area->nr_free--;
 665                 __mod_zone_page_state(zone, NR_FREE_PAGES, - (1UL << order));
 666                 expand(zone, page, order, current_order, area, migratetype);
 667                 return page;
 668         }
 669 
 670         return NULL;
 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.

 751 /* Remove an element from the buddy allocator from the fallback list */
 752 static struct page *__rmqueue_fallback(struct zone *zone, int order,
 753                                                 int start_migratetype)
 754 {
 755         struct free_area * area;
 756         int current_order;
 757         struct page *page;
 758         int migratetype, i;
 759 
 760         /* Find the largest possible block of pages in the other list */
 761         for (current_order = MAX_ORDER-1; current_order >= order;
 762                                                 --current_order) {
 763                 for (i = 0; i < MIGRATE_TYPES - 1; i++) {
 764                         migratetype = fallbacks[start_migratetype][i];
 765 
 766                         /* MIGRATE_RESERVE handled later if necessary */
 767                         if (migratetype == MIGRATE_RESERVE)
 768                                 continue;
 769 
 770                         area = &(zone->free_area[current_order]);
 771                         if (list_empty(&area->free_list[migratetype]))
 772                                 continue;
 773 
 774                         page = list_entry(area->free_list[migratetype].next,
 775                                         struct page, lru);
 776                         area->nr_free--;
 777 
 778                         /*
 779                          * If breaking a large block of pages, move all free
 780                          * pages to the preferred allocation list. If falling
 781                          * back for a reclaimable kernel allocation, be more
 782                          * agressive about taking ownership of free pages
 783                          */
 784                         if (unlikely(current_order >= (pageblock_order >> 1)) ||
 785                                         start_migratetype == MIGRATE_RECLAIMABLE) {
 786                                 unsigned long pages;
 787                                 pages = move_freepages_block(zone, page,
 788                                                                 start_migratetype);
 789 
 790                                 /* Claim the whole block if over half of it is free */
 791                                 if (pages >= (1 << (pageblock_order-1)))
 792                                         set_pageblock_migratetype(page,
 793                                                                 start_migratetype);
 794 
 795                                 migratetype = start_migratetype;
 796                         }
 797 
 798                         /* Remove the page from the freelists */
 799                         list_del(&page->lru);
 800                         rmv_page_order(page);
 801                         __mod_zone_page_state(zone, NR_FREE_PAGES,
 802                                                         -(1UL << order));
 803 
 804                         if (current_order == pageblock_order)
 805                                 set_pageblock_migratetype(page,
 806                                                         start_migratetype);
 807 
 808                         expand(zone, page, order, current_order, area, migratetype);
 809                         return page;
 810                 }
 811         }
 812 
 813         /* Use MIGRATE_RESERVE rather than fail an allocation */
 814         return __rmqueue_smallest(zone, order, MIGRATE_RESERVE);
 815 }

和__rmqueue_smallest相反,函数从大到小进行遍历。隐含的策略是作者认为选择更大的块,更容易避免碎片,因为不同迁移类型的内存块会混合起来。 
767 并不会尝试使用紧急分配的块,因为我们认为为紧急分配预留的块更重要,所以万不得以才分配。其实把紧急预留内存放在fallback最后不久好了吗?

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




你可能感兴趣的:(伙伴系统分配器 - buffered_rmqueue)