本人目前就职于烽火集成,从事云计算产品架构设计相关工作,长期专注于内核、虚拟化、分布式、云计算等方向。
技术交流请联系:xiaoding@fiberhome.com
前言
Virtio-Balloon驱动即内存气泡,可以用来动态调整内存,这个驱动很早就已经出来了,
最近才发现这个驱动居然还有监控内存指标的功能,所以再一次review了一遍代码。这里就把这个驱动详细的介绍一遍。
功能介绍
Virtio-Balloon驱动就是通过在虚拟机中申请内存,然后将申请的内存通知给qemu,然后qemu侧再将内存释放掉,可以让其他虚拟机使用,达到内存复用的能力。
要开启这个功能,就是在启动时候需要添加Virtio-Balloon后端设备,并且要在Guest里面安装
Virtio-Balloon驱动。
安装
- 添加设备
如果用libvirt启动 在libvirt的虚拟机配置侧添加如下xml
在qemu中启动中添加如下设备
- 安装驱动
windows驱动这里省略了在网上教程很多。
linux下在kernel 很早就加入了Virtio-Balloon驱动,所以在主流linux发行版中一般都具有这个驱动。
使用
有了上面的准备,启动虚拟机后就可以体验到内存气泡的功能。
在qemu的hmp中查看内存
- 设置目标虚拟机的内存,注意这里是直接设置虚拟机当前可以使用的内存。
比如起始的时候内存是8G,然后想缩减2G,实际这里操作要设置成目前为6G。
libvirt侧
在qemu的hmp中查看内存
代码分析
从代码侧来具体分析Virtio-Balloon的。先从linux驱动侧看起吧
相对其他virtio驱动 ,内存气泡的驱动相对简单好看。
它的代码全部哎driver/virtio/virtio_balloon.c中
首先来看 virtio_balloon_driver驱动的定义,
这里很容易看到驱动的处理函数并不多virtballoon_probe,在驱动加载时候,
virtballoon_remove在驱动卸载时候,剩下只有virtballoon_changed那么肯定所有的功能都是在这个里面来实现。
virtballoon_changed的函数也相当简单,就是启动了一个work
这个work主要来执行update_balloon_size_work 这个回调。
为了弄清楚这个work我们还是需要看一下virtio_balloon的初始化函数virtballoon_probe。
截取片段来看,初始化时候就注册了stats_request 这个callback,用来响应后端发来的stats请求。定义了2个任务 update_balloon_size_func是用来修改内存,
而update_balloon_stats_func这个是用来更新内存信息的,这个也是再次浏览代码时候的发现。
先来介绍基本功能update_balloon_size_func,代码非常好理解,拿到diff和本身现在的内存进行比较,然后开始增加或者减少内存,接着update_balloon_size更新下当前内存的信息。
fill_balloon和leak_alloon其实很相似,这里就看下fill_balloon的实现。
调用balloon_page_enqueue函数进行内存的添加,然后tell_host去更新表项。
balloon_page_enqueue函数是kernel自己实现的堆内存进行增减的功能。
所以无论是kvm还是xen的气球驱动,最终都是调用这个函数去进行实现。
该函数在mm/balloon_compaction.c。这里就不继续往下追了,本编只是介绍virtio-balloon驱动原理。要想更细一步了解内存的实现,在以后专门讲内存操作时会再次提及该函数。
这个基本功能介绍完了,回到本篇最初的问题,Virtio-Balloon驱动顺便还实现了一个内存的定时监控信息。
刚才在初始化的时候可以看到当后端主动调用stat指令时候,stats_request就会触发另一个work update_balloon_stats_func。
update_balloon_stats_func 这里主要调用stats_handle_request
然后update_balloon_stats这个函数将会收集所有的信息到达vb这个结构体,然后将vb发送给后端。
update_balloon_stats这个函数就是实际采集guest连内存使用的情况。
到此,Vritio-Balloon的linux驱动的相关实现都描述完了。
Windows驱动的实现和linux有些区别,这里还是有必要介绍一下。
在收到qemu的请求后,驱动会返回内存的值,这里值得注意的是,
sg.physAddr这里通过MmGetPhysicalAddress地址转换直接得到了内存使用率。
这个内存使用率的真正采集者其实是BLNSRV.exe这个进程。
这个进程中启动了定时任务,一直在刷新内存的使用率。
GatherKernelStats 实际采集内存指标
到此windows的驱动也讲完了。
qemu后端代码比较简单了,首先来看气球初始化时候,
通过qemu_add_balloon_handler,注册了在调用qmp时候的回调函数,用来发起请求。
初始化了队列,ivq,dvq,svq用来接收相关的信息,然后调用回调进行相应操作。
这里以刷新内存信息为例往下讲,内存增减功能类似。
在上面注册了guest-stats-polling-interval属性,这个是设置查询周期的,在设置了周期后,就可以看到启动了一个定时任务,balloon_stats_poll_cb来实时查询内存的信息。
而virtio_balloon_receive_stats主要是从vq中取到前面讲的内存信息,然后用balloon_stats_enabled进行更新。
总结
到此可以看到Virtio-Balloon的相关实现,和周期性查询内存信息的功能。
Virtio-Balloon进行内存复用本身存在一些问题
- Guest对内存变化会进行感知,Balloon特性本身是kernel所具备的,本身是通过修改识别的内存信息来限制Guest中的使用。所以不是很友好。
- 内存复用需要实时监控,发现客户虚拟机内存使用过多还要及时归还内存,对于系统本身做这个功能有很大的局限性。
- Balloon特性的内存复用并不是本质上进行内存的冗余复用,仅仅是借东墙补西墙,当虚拟机都大量使用内存时候,并不能实际突破物理内存上限。