我们知道buddy容易产生内存碎片,内核中可以通过给页面设置迁移类型以及compaction机制来预防和处理内存碎片。
内存页主要有下面三种类型:
1.可移动的(movable)。用户态申请。
2.可回收的(reclaimable)。文件系统的cache。
3.不可移动(unmovable)。内核申请用。
可移动的页面,顾名思义就是在被分配之后,还可以改变在物理内存中的位置。只要更新一下进程页表即可,进程不会感知物理页的移动。
那么,如果不可移动的页掺杂在了可移动的页里面,它附近的页面就没办法移动凑成连续页面了。例如整个内存有 0 1 2 3 4 5 六个连续的页面,如果3是不可移动,而其他可移动的话,那么再怎么移动也只有最多三个连续的页。
迁移类型
内核采用给页面设置一个迁移类型(migration type)来避免这个问题:
我们在伙伴系统初始化时讲过,Linux启动时默认所有页都是可移动的。当内核里想申请一页,不仅申请这一页,而是一下移出很高阶的buddy内存块(例如2^9个页)设置为不可移动的,后续内核申请不可移动页就从这里申请,这样就减少了各类型页面区域之间的干扰。
当然这块内存是动态释放和增加了,也就是说不可移动的页的范围并不是固定不变的。内核中维护了上述三种页的链表,在试图申请某个类型的页时,在期望类型的链表里申请不到页面的话,就寻求fallback类型的页面去申请,这时如果申请到内存了,就同时将这部分页面的类型改为为期望的类型(move_freepages_block()或move_freepages())。例如:
如果内核申请不可移动的页申请不到,就从可回收链表里拿,拿不到再从可移动链表里去拿。
用户空间申请的都是可移动的,直接从可移动的里面去拿,(有CMA的话,如果可移动页如果没有了,先从CMA里找,)如果找不到就从fallback链表找:先找可回收的,再找不可移动的。
这个fallback表就是struct zone的free_area成员里的free_list[MIGRATE_TYPES]。而fallback的顺序定义如下:
./kernel/linux-3.0.y/mm/page_alloc.c:
/*
* This array describes the order lists are fallen back to when
* the free lists for the desirable migrate type are depleted
*/
static int fallbacks[MIGRATE_TYPES][MIGRATE_TYPES-1] = {
[MIGRATE_UNMOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
[MIGRATE_RECLAIMABLE] = { MIGRATE_UNMOVABLE, MIGRATE_MOVABLE, MIGRATE_RESERVE },
[MIGRATE_MOVABLE] = { MIGRATE_RECLAIMABLE, MIGRATE_UNMOVABLE, MIGRATE_RESERVE },
[MIGRATE_RESERVE] = { MIGRATE_RESERVE, MIGRATE_RESERVE, MIGRATE_RESERVE }, /* Never used */
};
#define MIGRATE_UNMOVABLE 0
#define MIGRATE_RECLAIMABLE 1
#define MIGRATE_MOVABLE 2
#define MIGRATE_RESERVE 3
#define MIGRATE_ISOLATE 4 /* can't allocate from here */
#define MIGRATE_TYPES 5
memory-compaction
内核里的紧致内存机制,类似于磁盘碎片整理:把碎的页移动整合到连续的一段空间,就留出一段连续的内存了。在内核中打开CONFIG_COMPACTION就可以使用compaction功能了,要注意它只能整理可移动的页面。
可通过下面命令主动触发内存整理:
echo 1 > /proc/sys/vm/compact_memory
- 1
注意这个文件的权限是0200哦,只有root用户可以写。实际上写入的值没有意义,只作为一个触发器,内核不会判断这个值,而是直接开始对所有node所有zone尝试compaction。
在系统内存不足时,__alloc_pages_slowpath()在尝试了kswapd间接reclaim以及借助备用zone都无果后,也会先尝试compaction能否整理出够分配order的连续内存,如果不行才进行直接reclaim的。而对于/proc/sys/vm/compact_memory触发的整理,order=-1,即尽可能多的整理。
所以这两种方案各有其用处:
1. 迁移类型机制:预先将可移动的和不可移动的区分开,称为anti-fragmention。
2. memory-compaction:已经发生碎片了,通过整理变得不碎,称为defragmention。
即,迁移类型机制只是将内存按类型区分,预防碎片的发生,而不会做页面移动。memory-compation则负责在碎片发生后,对可移动页面进行整理。
页面迁移和compaction都会有开销的,即移动页的开销,如果你频繁的申请释放导致频繁的页面迁移或频繁的整理巨页,开销就很大了。因此一定要根据实际场景。
from: https://blog.csdn.net/jasonchen_gbd/article/details/79835655