KVM进程和普通进程基本一样,通过malloc或mmap系统调用分配动态分配内存。当云主机malloc 1g的内存时,它会很快分配1g的虚拟内存,但只有在实际写的时候,才会正真请求分配物理内存。云主机运行时,使用malloc分配的内存作为其物理内存。比如当它访问物理地址0x0时,那它实际访问malloc分配的的第一个page。[参考1]
KVM中内存也是允许过载使用(over-commit)的,KVM能够让分配给客户机的内存总数大于实际可用的物理内存总数。由于客户机操作系统及其上的应用程序并非一直100%地利用其分配到的内存,而且宿主机上的多个客户机一般也不会同时达到100%的内存使用率,所以内存过载分配是可行的。一般来说,有如下三种方式来实现内存的过载使用。
(1)气球(ballooning):通过virio_balloon驱动来实现宿主机Hypervisor和客户机之间的协作来完成。
(2)页共享(page sharing):通过KSM(Kernel Samepage Merging)合并多个客户机进程使用的相同内存页。UKSM(Ultra KSM)是在内核KSM基础上的优化版本。
(3)内存交换(swapping):用交换空间(swap space)来弥补内存的不足。
本文主要介绍后面两种方法以及相关实践方法。
ballooning是一种宿主机指示云主机释放一些已分配内存以用于其他用途的技术。此技术能帮助将内存压力重新从主机转移到客户上。气球中的内存是可以供宿主机使用的(但不能被客户机访问或使用),所以,当宿主机内存使用紧张,空余内存不多时,可以请求客户机回收利用已分配给客户机的部分内存,客户机就会释放其空闲的内存,此时若客户机空闲内存不足,可能还会回收部分使用中的内存,可能会换出部分内存到客户机的交换分区(swap)中,从而使得内存气球充气膨胀,从而让宿主机回收气球中的内存可用于其他进程(或其他客户机)。反之,当客户机中内存不足时,也可以让客户机的内存气球压缩,释放出内存气球中的部分内存,让客户机使用更多的内存。
优势:
1. Ballooning对内存的调节很灵活
2. 可以将从气球中得来的内存分配到任何需要的地方
缺点:
1. Ballooning需要客户机操作系统加载virtio_balloon驱动
2. 目前没有比较方便的、自动化的机制来管理ballooning
3. 内存的动态增加或减少,可能会使内存被过度碎片化
KSM(Kernel Samepage Merging)是内核自带的节省内存,去重的功能,ksm守候进程定期扫描用户注册的内存,寻找相同的页进行合并。内核文档中也提到,KSM最初就是为了kvm虚拟化中合并相同也来获得更多内存而开发的。 ksm只合并应用程序通过madvise指定的区域。
KVM中相关代码:
kvm-all.c:
kvm_setup_guest_memory(void *start, size_t size)
qemu_madvise(start, size, QEMU_MADV_DONTFORK);
osdep.c
qemu_madvise(start, size, QEMU_MADV_DONTFORK);
madvise(addr, len, advice);
内核配置:
CONFIG_KSM=y
控制接口:
控制接口目录:/sys/kernel/mm/ksm/
full_scans |
完成全扫描的次数 |
merge_across_nodes |
是否跨numa节点合并 |
pages_shared |
合并后的页数 |
pages_sharing |
合并前的页数 |
pages_to_scan |
每次扫描页 |
pages_unshared |
扫描单未合并的页 |
pages_volatile |
改变过快而无法合并的页 |
run |
0 停止ksmd但保留已经合并的页 1 运行ksmd 2 停止ksmd,取消所有合并的页 |
sleep_millisecs |
每次扫描间隔 |
pages_sharing /pages_shared 比值越高也好。
uksm是ksm的升级版,国人自主研发的一个 Linux 内核相关项目。
项目主页:http://kerneldedup.org/projects/uksm/
UKSM特性:
1 全系统扫描,扫描所有用户进程的匿名映射内存区。
2 较低的cpu占用率。
3 极速扫描,快速合并。
4 避免抖动区域。
使用方法:
打patch:
patch -p1 <../uksm-0.1.2.3-for-v3.14.ge.10.patch
内核配置:
Processor type and features --->
[*] Enable KSM for page merging
Choose UKSM/KSM strategy (Ultra-KSM for page merging) --->
控制接口:
优点:
如其特性中描述的,其扫描范围,合并速度都比KSM好很多
缺点:
1. 还没进入upstream。
2. 还存在bug,如下是遇到的一个编译BUG,及解决方法。
编译内核时报错:
mm/mmap.c: In function ‘exit_mmap’:
mm/mmap.c:2777:4: error: ‘struct mm_struct’ has no member named ‘mmap_cache’
解决:
@@ -2770,7 +2770,7 @@ void exit_mmap(struct mm_struct *mm)
mm->mmap = NULL;
mm->mm_rb = RB_ROOT;
- mm->mmap_cache = NULL;
+ vmacache_invalidate(mm);
up_write(&mm->mmap_sem);
WARN_ON(atomic_long_read(&mm->nr_ptes) >
除了内核自带swap功能,现在还活跃着很多zprojects,基本思想都是对要换出的页进行压缩,放在内存中,如有必要再写入swap分区。这些zprojects 包括zram,zswap,zcache等。
项目原名compcache,
主页:https://code.google.com/p/compcache/
3.14内核正式加入upstream,但去掉了后备盘的功能。
原理:
分析zram代码,可以看出,zram主要是虚拟一个内存盘,然后通过swapon将该盘设置为swap盘。在写swap盘的过程中,对page进行了压缩。
1. 初始化:
register_blkdev(0, "zram");
create_device(&zram_devices[dev_id], dev_id);
//注册q->make_request_fn
blk_queue_make_request(zram->queue, zram_make_request);
zram->disk = alloc_disk(1);
//整个初始化过程其实就是一个简单的块设备驱动的初始化流程
2. 当内核swap读写数据:
submit_bio
generic_make_request(bio);
do {
struct request_queue *q = bdev_get_queue(bio->bi_bdev);
q->make_request_fn(q, bio);
} while (bio);
3. 最终调用初始化时注册的make_request方法:
zram_make_request
__zram_make_request(zram, bio, bio_data_dir(bio));
bio_for_each_segment(bvec, bio, i) {
zram_bvec_rw(zram, &bv, index, offset, bio, rw)
//读写时,先进行压缩解压的操作
zram_bvec_read(zram, bvec, index, offset, bio);
zram_decompress_page(zram, uncmem, index);
zram_bvec_write(zram, bvec, index, offset);
lzo1x_1_compress();
}
使用方法:
1. modprobe zram num_devices=4 //加载模块,设置内存盘数目
设置后出现4个设备 /dev/zram{0,1,2,3}
2. 通过/sys/block/zramX/disksize设置内存盘大小
3. 将内存盘设置为swap磁盘
mkswap /dev/zram0
swapon /dev/zram0
缺点:
该项目为加入内核,已将后备盘功能去掉,不建议使用。
zswap是一个轻量级的换出页缓存,采用frontswap路径,在page进入swap流程前,先进行压缩,压缩后的页保存在自己维护的动态内存池中。当内存池达到预设的容量时,它还能降压缩的页推入swap设备。内核3.11版本引入。
swap_writepage(struct page *page, struct writeback_control *wbc)
if (frontswap_store(page) == 0) {
}
__swap_writepage(page, wbc, end_swap_bio_write);
frontswap_store
frontswap_ops->store(type, offset, page);
//该函数在zswap初始化时注册
zswap_frontswap_store
zswap_comp_op(ZSWAP_COMPOP_COMPRESS, src, PAGE_SIZE, dst, &dlen);
zswap_rb_insert(&tree->rbroot, entry, &dupentry);
使用方法:
zswap配置主要通过内核启动参数来配置:
1. zswap.enabled=1 开启zswap
2. zswap.max_pool_percent 设置zswap使用的内存池占总内存的百分比。
3. zswap.compressor 设置zswap采用的页压缩算法
优点:
目前该功能已经加入upstream.但还处于experimental阶段。
缺点:
zswap功能与ksm、uksm的结合性还不是很确定。
以上几种方案各有利弊,还需要进行充分的测试。目前计划测试一下几种方案:
1. ksm、uksm
2. zswap (scsi swap )
3. zswap+ssd
4. ssd as swap
测试内容及工具:
1. 测试纯内存应用:memcached,当云主机内存超售时,测试其get、set的速度。
2. 测试文件io: fio,当云主机内存接近饱和时,测试内存超售对文件io的影响
3. 测试网络:netperf,当云主机内存接近饱和时,测试内存超售对网络的影响。
4. 记录测试过程中,云主机,宿主机cpu状态,内存状态,及swap盘的读写状态。可以采用sar工具。
1 http://www.linux-kvm.org/page/Memory
2 Documentation/vm/zswap.txt
3 Documentation/blockdev/zram.txt
网易云大礼包:https://www.163yun.com/gift
本文来自网易实践者社区,经作者刘长伟授权发布