KVM虚拟化介绍及代码分析【转】

KVM虚拟化

1.1 KVM技术基础

KVM(kernel-based virtual machine)的名字,基于kernel的虚拟机,已经很准确的说出了kvm的设计思路:也就是依赖linux内核,完全利用linux内核来实现cpu的调度,内存管理的功能。而另一个开源虚拟机xen,则自己开发了一套底层操作系统功能。从vcpu调度到内存管理一应俱全。虽然xen这个系统也是基于linux的,但是发展路线不同,和目前linux内核相比,已经面目全非了。这就是kvm受到开源组织的欢迎,而xen一直被排斥的根源。

虽然说早期的kvm是全虚拟化,而xen是半虚拟化,但发展到今天,xen支持全虚拟化,而kvm也早就有了半虚拟化的patch。技术上可以互相渗透,而软件架构一旦确定了,反而难改。不能因为xen是半虚拟化,就认为linux内核排斥半虚拟化的方案。实际上,另一个进了内核的开源虚拟机Lguest,它就是一个半虚拟化的方案。当然,现在linux内核本身都推出了半虚拟化架构,做半虚拟化也没以前那么繁琐了。

另一个趋势是基于硬件的虚拟化成为主流。早期x86虚拟化的低性能让人印象深刻,所以在intel推出硬件辅助虚拟化之后,虚拟化方案全面向硬件辅助靠拢。而kvm,Lguest这些比较新的方案,则彻底不支持软件的方案,而把硬件辅助当作了设计的根基。

从软件架构上来说,kvm提供了两个内核模块,使用kvm的io_ctl接口可以管理vcpu和内存,为vcpu注入中断和提供时钟信号,而kvm本身没有提供设备的模拟。设备模拟需要应用层软件Qemu来实现。这种架构保证了kvm避免了繁琐的设备模拟和设备驱动部分(内核中80%以上的代码就是驱动部分)。
总结一下kvm软件的架构特点:

  • Kvm本身只提供两个内核模块。Kvm实现了vcpu和内存的管理。
  • Qemu控制逻辑,负责创建虚拟机,创建vcpu。

1.2 KVM管理接口

Qemu和kvm关系很深,甚至可以认为双方本来是一个软件,Qemu是应用层的控制部分,而kvm是内核执行部分。软件复用能达到如此天衣无缝的地步,是一件很神奇的事情,也说明kvm设计时候的思路之巧。

所以分析kvm,必须首先从Qemu的代码分析入手。为了避免繁琐,引入太多知识点,而混杂不清。所以把Qemu的代码做简化处理。

QEMU启动代码

	//通过Qemu打开设备/dev/kvm
    s->fd = qemu_open("/dev/kvm", O_RDWR);
    ret = kvm_ioctl(s, KVM_GET_API_VERSION, 0);
    
    //通过KVM_CREATE_VM创建一个虚拟机对象VM
    s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0);
	...
    //然后通过KVM_CREATE_VCPU为虚拟机创建vcpu对象
    ret = kvm_vm_ioctl(s, KVM_CREATE_VCPU, env->cpu_index);
	...
    env->kvm_fd = ret;
    //最后通过KVM_RUN设置vcpu运行起来
    run_ret = kvm_vcpu_ioctl(env, KVM_RUN, 0);

可以看到,kvm提供了一个设备/dev/kvm,对kvm的控制要通过这个设备提供的io_ctl接口实现。这是linux内核提供服务的最通用方式,不再赘述。
而kvm提供了三种概念,分别通过不同的io_ctl接口来控制。

  • kvm:代表kvm模块本身,用来管理kvm版本信息,创建一个vm。
  • vm:代表一个虚拟机。通过vm的io_ctl接口,可以为虚拟机创建vcpu,设置内存区间,创建中断控制芯片,分配中断等等。
  • vcpu:代表一个vcpu。通过vcpu的io_ctl接口,可以启动或者暂停vcpu,设置vcpu的寄存器,为vcpu注入中断等等。

Qemu的使用方式,首先是打开/dev/kvm设备,通过KVM_CREATE_VM创建一个虚拟机对象,然后通过KVM_CREATE_VCPU为虚拟机创建vcpu对象,最后通过KVM_RUN设置vcpu运行起来。因为是简化的代码,中断芯片的模拟,内存的模拟,寄存器的设置等等都已经省略了。

1.3 VT技术和vmcs结构

前文讲到kvm是基于硬件辅助虚拟化来实现的。这个硬件辅助的虚拟化技术,在不同的cpu架构中有不同的实现。在x86的平台下,intel实现了VT技术,而另一家x86芯片厂家AMD也推出了自己的虚拟化技术AMD-V。反映到代码上,intel技术的代码都在/arch/x86/kvm目录里面的vmx.c文件,而AMD的实现代码在相同目录的svm.c文件中。

回顾一下虚拟化技术的实现,经典的虚拟化使用了陷入-模拟的模式,而硬件辅助虚拟化引入了根模式(root operation)和非根模式(none-root operation),每种模式都有ring0-3的四级特权级别。所以,在硬件辅助虚拟化中,陷入的概念实际上被VM-EXIT操作取代了,它代表从非根模式退出到根模式,而从根模式切换到非根模式是VM-Entry操作。

1.3.1 需要具备的硬件知识

做系统软件的必须和硬件打交道,这就必须深入cpu架构和设备的架构。但是intel的架构浩大繁杂,说明文档多达上千页,深入了解着实有难度,另外一种趋势是软硬件的分离已经进行了多年,而系统软件的作者多半是软件人员,而非硬件人员。作为软件人员,了解必备的硬件知识是需要的,也是理解代码和架构的基础。同时,在操作系统软件的理解中,分清软件部分的工作和硬件部分的工作是必备条件,这也是操作系统软件中最让人困惑的部分。

对于虚拟化的vt技术而言,它的软件部分基本体现在vmcs结构中(virtual machine control block)。主要通过vmcs结构来控制vcpu的运转。

  • Vmcs是个不超过4K的内存块。
  • Vmcs通过下列的指令控制,vmclear:清空vmcs结构,vmread:读取vmcs数据,vmwrite:数据写入vmcs
  • 通过VMPTR指针指向vmcs结构,该指针包含vmcs的物理地址。

Vmcs包含的信息可以分为六个部分。

  • Guest state area:虚拟机状态域,保存非根模式的vcpu运行状态。当VM-Exit发生,vcpu的运行状态要写入这个区域,当VM-Entry发生时,cpu会把这个区域保存的信息加载到自身,从而进入非根模式。这个过程是硬件自动完成的。保存是自动的,加载也是自动的,软件只需要修改这个区域的信息就可以控制cpu的运转。
  • Host state area:宿主机状态域,保存根模式下cpu的运行状态。只在vm-exit时需要将状态
  • VM-Execution control filelds:包括page fault控制,I/O位图地址,CR3目标控制,异常位图,pin-based运行控制(异步事件),processor-based运行控制(同步事件)。这个域可以设置那些指令触发VM-Exit。触发VM-Exit的指令分为无条件指令和有条件指令,这里设置的是有条件指令。
  • VM-entry contorl filelds:包括vm-entry控制,vm-entry MSR控制,VM-Entry插入的事件。MSR是cpu的模式寄存器,设置cpu的工作环境和标识cpu的工作状态。
  • VM-exit control filelds:包括VM-Exit控制,VM-Exit MSR控制。
  • VM退出信息:这个域保存VM-Exit退出时的信息,并且描述原因。

有了vmcs结构后,对虚拟机的控制就是读写vmcs结构。后面对vcpu设置中断,检查状态实际上都是在读写vmcs结构。在vmx.h文件给出了intel定义的vmcs结构的内容。

1.4 CPU虚拟化

1.4.1 VCPU创建

首先是Qemu创建VM,从代码分析一下:

static int kvm_dev_ioctl_create_vm(void)
{
	int fd;
	struct kvm *kvm;

	kvm = kvm_create_vm();
	if (IS_ERR(kvm))
		return PTR_ERR(kvm);
    /*生成kvm-vm控制文件*/
	fd = anon_inode_getfd("kvm-vm", &kvm_vm_fops, kvm, 0);
	if (fd < 0)
		kvm_put_kvm(kvm);

	return fd;
}

调用了函数kvm_create_vm,然后是创建一个文件,这个文件的作用是提供对vm的io_ctl控制。

static struct kvm *kvm_create_vm(void)
{
	struct kvm *kvm = kvm_arch_create_vm();
    /*设置kvm的mm结构为当前进程的mm,然后引用计数加一*/
	kvm->mm = current->mm;
	...
    /*把kvm链表加入总链表*/
	list_add(&kvm->vm_list, &vm_list);
	spin_unlock(&kvm_lock);

	return kvm;
}

可以看到,这个函数首先是申请一个kvm结构。然后执行初始化工作。

初始化第一步是把kvm的mm结构设置为当前进程的mm。我们知道,mm结构反应了整个进程的内存使用情况,也包括进程使用的页目录信息。

然后是初始化io bus和eventfd。这两者和设备io有关。

最后把kvm加入到一个全局链表头。通过这个链表头,可以遍历所有的vm虚拟机。

创建VM之后,就是创建VCPU。

static int kvm_vm_ioctl_create_vcpu(struct kvm *kvm, u32 id)
{
	int r;
	struct kvm_vcpu *vcpu, *v;
	
	/*调用相关cpu的vcpu_create*/
	vcpu = kvm_arch_vcpu_
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

xidianjiapei001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值