Linux内核内存管理( 九 )


在整合内存碎片的过程中,碎片页只会在本zone的内部移动,将位于zone低地址的页尽量移到zone的末端 。申请新的页面位置通过函数实现 。
移动过程又分为同步和异步,内存申请失败后第一次将会使用异步,后续之后将会使用同步 。同步过程只移动当面未被使用的页,异步过程将遍历并等待所有的页使用完成后进行移动 。
2 按可移动性组织页
按照可移动性将内存页分为以下三个类型:
:在内存中位置固定,不能随意移动 。分配的内存基本属于这个类型;
:不能移动,但可以删除回收 。例如文件映射内存;
:可以随意移动,用户空间的内存基本属于这个类型 。
申请内存时,根据可移动性,首先在指定类型的空闲页中申请内存,每个zone的空闲内存组织方式如下:
zone {

[];

{
[];
long ;
};
当在指定类型的申请不到内存时,可以从备用类型挪用,挪用之后的内存就会释放到新指定的类型列表中,把这个过程称为“盗用” 。
备用类型优先级列表如下定义:
static int fallbacks[MIGRATE_TYPES][4] = {[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE,MIGRATE_RESERVE },[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE,MIGRATE_MOVABLE,MIGRATE_RESERVE },#ifdef CONFIG_CMA[MIGRATE_MOVABLE] = { MIGRATE_CMA,MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },[MIGRATE_CMA] = { MIGRATE_RESERVE }, /* Never used */#else[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE,MIGRATE_RESERVE },#endif[MIGRATE_RESERVE] = { MIGRATE_RESERVE }, /* Never used */#ifdef CONFIG_MEMORY_ISOLATION[MIGRATE_ISOLATE] = { MIGRATE_RESERVE }, /* Never used */#endif};
值得注意的是并不是所有场景都适合按可移动性组织页,当内存大小不足以分配到各种类型时,就不适合启用可移动性 。有个全局变量来表示是否启用,在内存初始化时设置:
void __ref build_all_zonelists(pg_data_t *pgdat, struct zone *zone){......if (vm_total_pages < (pageblock_nr_pages * MIGRATE_TYPES))page_group_by_mobility_disabled = 1;elsepage_group_by_mobility_disabled = 0;......}如果page_group_by_mobility_disabled,则所有内存都是不可移动的 。其中有个参数决定了每个内存区域至少拥有的页,pageblock_nr_pages,它的定义如下:#define pageblock_order HUGETLB_PAGE_ORDER#else /* CONFIG_HUGETLB_PAGE *//* If huge pages are not used, group by MAX_ORDER_NR_PAGES */#define pageblock_order (MAX_ORDER-1)#endif /* CONFIG_HUGETLB_PAGE */#define pageblock_nr_pages (1UL << pageblock_order)
在系统初始化期间,所有页都被标记为:
void __meminit memmap_init_zone(unsigned long size, int nid, unsigned long zone,unsigned long start_pfn, enum memmap_context context){......if ((z->zone_start_pfn <= pfn)&& (pfn < zone_end_pfn(z))&& !(pfn & (pageblock_nr_pages - 1)))set_pageblock_migratetype(page, MIGRATE_MOVABLE);......}
其它可移动性类型的页都是后来产生的,也就是前面说的“盗取” 。在这种情况发生时,通常会“盗取”中更高优先级、更大块连续的页,从而避免小碎片的产生 。
/* Remove an element from the buddy allocator from the fallback list */static inline struct page *__rmqueue_fallback(struct zone *zone, int order, int start_migratetype){....../* Find the largest possible block of pages in the other list */for (current_order = MAX_ORDER-1; current_order >= order;--current_order) {for (i = 0;; i++) {migratetype = fallbacks[start_migratetype][i];......}
可以通过/proc/查看当前系统各种类型的页分布 。