内存管理对于不同的zone管理区,都会划分为不同的memory block进行管理,这些memory block是按照2的幂次个页面进行管理的,对应的结构体如下:
struct zone {
...
/* free areas of different sizes */
struct free_area free_area[MAX_ORDER];
...
}
struct free_area {
struct list_head free_list[MIGRATE_TYPES];
unsigned long nr_free;
};
这里的每个free_area结构体中都管理着大小相同的连续内存块,不同块之间使用list连接。内核较早的版本对于free_list不会做migrate_type的区分,后来引入migrate_type也是为了优化内存管理中的碎片化问题。
怎么来理解这个优化特性呢?
这里的套路和内核中引入ZONE_MOVABLE类似,当内存区中如果按照内存页是否可以被回收,是否可以被迁移等特性进行分类时,将避免了相互干扰,后期进行内存回收时将会获取更多的连续内存,这也是终极目的。
关于ZONE_MOVABLE的介绍参考我的另一篇博客:对于ZONE_MOVABLE的理解。
接下来看一下这里对页面是如何进行分类的:
enum {
MIGRATE_UNMOVABLE,
MIGRATE_RECLAIMABLE,
MIGRATE_MOVABLE,
MIGRATE_PCPTYPES, /* the number of types on the pcp lists */
MIGRATE_RESERVE = MIGRATE_PCPTYPES,
#ifdef CONFIG_CMA
/*
* MIGRATE_CMA migration type is designed to mimic the way
* ZONE_MOVABLE works. Only movable pages can be allocated
* from MIGRATE_CMA pageblocks and page allocator never
* implicitly change migration type of MIGRATE_CMA pageblock.
*
* The way to use it is to change migratetype of a range of
* pageblocks to MIGRATE_CMA which can be done by
* __free_pageblock_cma() function. What is important though
* is that a range of pageblocks must be aligned to
* MAX_ORDER_NR_PAGES should biggest page be bigger then
* a single pageblock.
*/
MIGRATE_CMA,
#endif
#ifdef CONFIG_MEMORY_ISOLATION
MIGRATE_ISOLATE, /* can't allocate from here */
#endif
MIGRATE_TYPES
};
这里把不同特性的内存块分类放到不同的链表中管理,比如MIGRATE_RECLAIMABLE链表中存放的都是可以直接回收的内存,一般是文件缓存页,MIGRATE_MOVABLE链表中存放的都是可以迁移的内存块,MIGRATE_CMA链表中存放的是和CMA共同使用的区域,该区域也要求必须可以被迁移,这样当外设驱动申请CMA内存时(一般用于DMA传输大块物理连续内存),伙伴系统能够及时的腾出空间给DMA使用。
当系统中剩余内存很多但不连续时,如果申请一个大块连续内存依然会导致失败,如果能够把不连续的内存通过迁移的手段进行规整,把空闲内存组合成一块连续内存,那就可以在一定程度上达到内存申请的需求,如果该区域中所有页面都可以迁移,那么问题就变得简单了,所以内核就按照页面是否可以迁移做了区分,保证了在MIGRATE_MOVABLE链表中都是可以迁移的页面,这样可以尽可能的利用页面迁移规整出大块的连续内存。
参考文章:
https://www.cnblogs.com/linhaostudy/p/10482439.html
https://blog.youkuaiyun.com/dog250/article/details/6108028