在__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 一切都失败后,从紧急分配的预留链表进行分配。