KVM/QEMU虚拟化简介

作者:Diting0x


CSysSec注: 本文来自Diting0x个人博客,主要介绍KVM/QEMU虚拟化基础,对认识KVM与QEMU之间的关系有很大帮助。
转载本文请务必注明,文章出处:《KVM/QEMU虚拟化简介》与作者信息:Diting0x


  • 0X01 KVM/Qemu选择与部署
  • 0X02 KVM/Qemu原理概览

0X01 KVM/Qemu选择与部署

KVM作为虚拟机监控器VMM,分为两部分,分别是运行于kernel模式的KVM内核模块(kvm-kmod)和运行于user模式的Qemu模块。KVM的具体实现下文会作简单介绍。先来看kvm-kmod部分,linux kernel从2.6版本开始便开始集成了KVM模块,如果你只想简单的使用KVM,只需简单编译和配置内科即可使用KVM的一切特性,这里不作介绍,如果你想基于KVM做开发,还是老老实实去kvm官网下载,自己源码安装吧,在选择KVM版本之前,可以打开任意一个版本例如kvm-kmod-3.8里面的configure文件,可看到以下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# minimum is 2.6.x
min_kernel_version=24
# maximum is 3.x
max_kernel_version=8
kernel_version=`echo $kernel_version_str | sed 's/\([0-9]*\)\.[0-9]*\.[0-9]*.*/\1/'`
kernel_patchlevel=`echo $kernel_version_str | sed 's/[0-9]*\.\([0-9]*\)\.[0-9]*.*/\1/'`
kernel_sublevel=`echo $kernel_version_str | sed 's/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/'`
if [ ! -n "$force_build" ]; then
if [ $kernel_version -eq 2 ] && [ $kernel_sublevel -lt $min_kernel_version ]; then
echo
echo "Error: kernel is too old for this kvm-kmod release."
echo
exit 1
elif [ $kernel_version -eq 3 ] && [ $kernel_patchlevel -gt $max_kernel_version ]; then
echo
echo "Error: kernel is more recent than KVM modules delivered with this release."
echo "You probably want to use the KVM support that is already part of your kernel."
echo
exit 1
fi
fi

这段代码仔细看看,就不难看出kvm-kmod版本的每个数字和kernel版本号都是有关联的,kvm-kmod毕竟是一个内核模块,必须去适应内核,大家如果在安装kvm-kmod时出现kernel is too old 或者kernel is more recent等错误就应该静下来好好看看这段代码了。下载后匹配的kvm-kmod后,安装很简单,只需要执行./configure make $$ make install即可

安装之后,有两个模块kvm.ko kvm-intel.ko便会生成在./kvm/x86文件夹中,执行insmod kvm.ko kvm-intel.ko 或者modprobe kvm.ko kvm-intel.ko,便成功将kvm模块加载到内核了,
如果期间出现错误,可执行lsmod | grep kvm 看kvm是否成功加载,或执行dmesg | grep kvm查看具体执行信息。
这里强调,kvm-kmod是内核的一个模块,可随时加载随时删除,因此基于kvm的开发,修改kvm的源码之后,编译与运行起来都很方便。

qemu-kvm是为兼容kvm基于qemu模拟器开发出来的一个分支版本,安装qemu-kvm之前,检查是否有以下依赖包: linux-kernel-headers zlib1g-dev libglib2.0-dev,如果没有apt-get install一下。
之后,执行:

./configure --prefix=/usr/local/kvm 
make && make install

即可,qemu-kvm便会安装到/usr/local/kvm路径下,安装成功后,会在此路径的/bin文件夹下出现qemu-img, qemu-system-x86_64等二进制文件,自己也可以选择将这些二进制文件拷贝到根目录下的/bin文件夹中,这样在执行这些二进制文件的时候就不用加前缀/usr/local/kvm/bin了。
执行:

/usr/local/kvm/bin/qemu-img create -f qcow2 imgname.img 10G 

创建一个qcow2格式的空虚拟机img文件
执行:

/usr/local/kvm/bin/qemu-system-x86_64 -hda imgname.img -m 1024 -vnc :1 -cdrom imgname.iso -boot d     

将imgname.iso系统安装到imgname.img中
之后利用xvnc4viewer等vnc软件连接虚拟机完成安装过程,安装完后,执行:

/usr/local/kvm/bin/qemu-system-x86_64 -hda imgname.img -vnc :1 -m 1024 -monitor stdio  

登陆到虚拟机。具体细节可自行摸索。

0X02 KVM/Qemu原理概览

说说kvm与qemu的关系,借用实验室某大神给的一张图:

kvm-qemu-rela

可以看到,这里有三种模式,第一是客户机执行时所处的Guest模式,也就是虚拟化技术VMX中的非Root模式;第二是KVM运行的Kernel模式,即VMX中的Root模式,此时特权级为0;第三是Qemu运行的User模式,处于VMX Root模式中的特权级3.有关VMX技术,以及非Root模式如何通过VM Exit进入到Root模式,Root模式如何通过VM Entry进入到非Root模式,可参考Intel系统编程手册。

简单说说Qemu所在的User模式如何与KVM所在的kernel模式交互,虚拟化中的VT-x技术的支持,使得KVM可以虚拟出多个虚拟处理器VCPU, 而这些VCPU对应每一个Qemu线程,VCPU的创建、初始化、运行以及退出都是在Qemu线程的上下文中进行,这些过程都是通过Qemu向KVM发送一个I/O通道管理函数ioctl来完成,以qemu-kvm-1.1.2为例,qemu-kvm中首先会在./linux-header/linux/kvm.h注册相应的ioctl,如#define KVM_GET_REGS _IOR(KVMIO, 0x81, struct kvm_regs),各参数含义可以具体去了解ioctl的实现方式,之后调用kvm_vcpu_ioctl(env,KVM_GET_REGS,&regs)(有些也会调用kvm_vm_ioctl),KVM-Kmod中也会注册相应的ioctl,之后真正执行时会调用KVM_Kmod的相应函数,一般会在kvm_main.c文件中定义。如果想自定义自己的函数,需要分别在qemu-kvm与kvm-kmod注册相应的ioctl,只要编号不重复即可,注册后再调用自己定义的函数。

备注:Qemu 线程以 ioctl 的方式向 KVM 内核模块发出指 示,后者执行 VM entry 操作,将处理器由 kernel 模式切换到 Guest 模式,中止宿主机软件, 转而运行客户软件。注意,宿主机软件被中止时,正处于 Qemu 线程上下文,且正在执行 ioctl 系统调用的 kernel 模式处理程序。客户软件在运行过程中,如发生异常或外部中断等事件, 或执行 I/O 操作,可能导致 VM exit,将处理器状态由 Guest 模式切换回 Kernel 模式。KVM 内核模块检查发生 VM exit 的原因,如果 VM exit 由于 I/O 操作导致,则执行系统调用返回操 作,将 I/O 操作交给处于 User 模式的 Qemu 线程来处理,Qemu 线程在处理完 I/O 操作后再 次执行 ioctl,指示 KVM 切换处理器到 Guest 模式,恢复客户软件的运行;如果 VM exit 由于 其它原因导致,则由 KVM 内核模块负责处理,并在处理后切换处理器到 Guest 模式,恢复 客户机的运行。

作者@Diting0x

于2016年1月1日West Lafayette, Lawson Computer Science Building.


转载本文请务必注明,文章出处:《KVM/QEMU虚拟化简介》与作者信息:Diting0x