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