我们知道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的顺序定义如下: