在__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相反,函数从大到小进行遍历。隐含的策略是作者认为选择更大的块,更容易避免碎片,因为不同迁移类型的内存块会混合起来。
814 一切都失败后,从紧急分配的预留链表进行分配。