前言
内存碎片分为内部碎片和外部碎片,内部碎片指内存页里面的碎片,外部碎片指空闲的内存页分散,很难找到一组物理地址连续的空间内存页,无法满足超过一页的内存分配请求。
虚拟可移动区域
可移动区域(ZONE_MOVABLE)是一个伪内存区域,基本思想很简:把物理内存分为两个区域,一个区域用于分配不可移动的页,另一个区域用于分配可移动的页,防止不可移动的页向可移动的页区域引入碎片。
技术原理为:
可移动区域(ZONE_ MOVABLE)没有包含任何物理内存,所以我们说它是伪内存区域,或者说是虚拟的内存区域。
可移动区域借用最高内存区域的内存,在32位系统上最高的内存区域通常是高端内存区域(ZONE_ HIGHMEM),
在64位系统上最高的内存区域通常是普通区域(ZONE NORMAL)。
区域大小设置:
内核引导参数kernelcore=xx指定不可移动区域大小,也可使用kernelcore=mirror指定使用镜像内存,其他区域作为可移动区域。
内核引导参数movablecore=xx指定可移动区域大小。
如果同时设置kernelcore,movablecore,那么不可移动区域大小取kernelcore和(物理内存大小-movablecore)的最大值。.
在NUMA系统上,宏CONFIG_MOVABLE_NODE,并指定内核引导参数movable_node,
那么忽略内核引导参数kernelcore和movablecore,所有可以热插拔的物理内存都可作为移动区域。
可移动区域 没有包含任何物理内存,是虚拟的内存区域。可移动区域借用最高内存区域的内存,32位系统通常是高端内存区域(ZONE_HIGHMEM),64位系统最高内存区域通常是(ZONE_NORMAL)。
内存区域
这里强调一点 没找到这个可移动区域的定义是啥 之前学的内存区域都是指的为物理ram的区域
在虚拟内存只有用户空间和内核空间的划分 也没听过区域
下面只能回顾一下物理ram的区域划分 还是x86的 但差不多
物理内存被分为三个区域:ZONE_DMA,ZONE_NORMAL,ZONE_HIGHMEM。
DMA和HIGHMEM区域不是必须存在的,在Kernel编译选项中可控制开关。
在ARM架构中,通常是不存在ZONE_DMA区域的,ZONE_HIGHMEM的起始地址默认是760M,而不是x86的896M。
ZONE_DMA和ZONE_NORMAL是直接映射到Kernel地址空间的,所以它们的物理地址可以通过一个偏移量直接转化为Kernel地址。
ZONE_HIGHMEM区域没有直接映射,Kernel无法直接访问,需要通过vmalloc映射到Kernel空间。vmalloc的用途是将非连续的物理空间映射为连续的Kernel地址空间,vmalloc会优先使用highmem。
在64位系统中,Kernel可寻址的空间超过512G,所以也就不需要ZONE_HIGHMEM。
内存碎片整理
从内存区域的底部扫描已分配的可移动页,从内存区域的顶部扫描空闲页,把底部的可移动页移动到顶部空闲页,在底部形成连续的空闲页。
整理算法:
1 首先从内存区域底部向顶部以页块为扫描单位,在页块内部起始页向结束页扫描,把这个页块里面的可移动页组成一条链表;
2 然后从内存区域顶部向底部以页块为单位扫描,在页块内部从起始页向结束页扫描,把空闲页组成一条链表;
3 最后把底部的可移动页的数据复制到顶部的空闲页,修改进程的页表,把虚拟页映射到新物理页。
假如有16个页,白色表示空闲页。这个内存区域已经碎片化,最大的连续页是两页。从这个区域内存分配3页就会失败,甚至分配两页也会失败,因为连续的空闲页的起始地址没有对齐到两页的整数倍。
内存碎片整理优先级
完全同步模式(COMPACT_PRIO_SYNC_FULL):允许阻塞,允许把脏的文件页回写到存储设备上,并且等回写完成
轻量级同步模式(COMPACT_PRIO_SYNC_LIGHT):允许大多数操作阻塞,但是不允许把脏数据回写到存储设备上;
异步模式(COMPACT_PRIO_ASYNC):不允许阻塞。
成本:完全同步模式>轻量级同步>异步模式
什么时候会进行碎片整理
1 页分配器使用最低水线分屏页失败以后,如果调用者允许直接回收页和写存储设备,并且是最昂贵的分配或者申请不可移动类型的连续页,那么在尝试直接回收页之前,先尝试执行异步模式的内存碎片整理。
2 页分配器直接回收以后连续分配页仍然失败,如果调用者允许写存储设备,尝试执行轻量级同步模式的内存碎片整理。
3 每个内存节点有一个页回收线程和一个内存碎片整理线程,页回收线程准备睡眠小段时间的时候,唤醒内存碎片整理线程,内存碎片整理线程执行轻量级同步模式的内存碎片整理。
4 系统管理员向文件/proc/sys/vm/compact_memory写入任何整数值的时候,在所有内存节点的所有区域上执行完全同步的内存碎片整理。
内存碎片整理线程名“kcompactd<node_id>”,内存节点的pglist_data的实例成员"kcompactd"指向内存碎片整理线程的进程描述符。
什么时候结束碎片整理
如果迁移扫描器和空闲扫描器相遇,那么内存碎片整理结束。
如果迁移扫描器和空闲扫描器没有相遇,但是申请或备用的迁移类型至少有一个足够大的空闲页块,那么内存碎片整理结束。
具体操作
编译内核时
如果需要内存碎片整理功能,需要配置CONFIG_COMPACTION
命令行下
//设置外部碎片的阈值
root@ubuntu:/home/wy/misc/net# cat /proc/sys/vm/extfrag_threshold
500
//是否允许内部碎片整理移动不可回收的页,1 允许
root@ubuntu:/home/wy/misc/net# cat /proc/sys/vm/compact_unevictable_allowed
1
//a.写入任何值触发内存碎片整理
root@ubuntu:/home/wy/misc/net# cat /proc/sys/vm/compact_memory
cat: /proc/sys/vm/compact_memory: Permission denied
//b.文件proc/sys/vm/compact_unevictable_allowed:此文件用来是否允许内存碎片整理移
//动不可回收的页(进程使用系统调用mlock把页锁定在内存当中),如果设置为1,表示允许,默认
//值是1。
//C.文件"proc/sys/vm/extfrag_ threshold":用来设置外部碎片的阈值,取值范围是0-1000。默
认值是500。
怎么判断我们要不要进行碎片整理
1.按照上面说的我们手动进行整理,系统管理员发指令了 那就要整理
2.b.如果内存区域同步满足3个条件,适合执行内存碎片整理:
1)如果(空闲页数-申请页数)低于水线,或者虽然大于或等于水线但是没有一个足够大的空
闲页块,那么这个内存区域适合执行内存碎片整理。
2)如果(空闲页数两倍的申请页数)大于或等于水线,说明有足够多的空闲页作为迁移的
目的地,那么这个内存区域适合执行内存碎片整理。
3)对于昂贵的分配(除数大于3) ,计算碎片指数,如果碎片指数在范围[0,外部碎片的阈
值]以内,说明分配失败是内存不足够导致的,不是外部碎片导致的,那么这个内存区域不适合执行
内存碎片整理。