KVM的初始化过程

 之前打算整理一下在Guest VM, KVM, QEMU中IO处理的整个流程,通过查阅资料和阅读源码,已经大致知道IO在Guest KVM中的处理流程.当想要整理IO在KVM和QEMU中的处理时,发现很难理清楚QEMU和KVM之间的跳转和交互的过程,于是促使自己去了解QEMU和KVM启动的过程.(本文展示的代码中,qemu版本为1.6.0, linux内核版本为3.7.10)

    为了介绍qemu和kvm的交互过程,我首先介绍一下kvm给用户提供的接口.kvm是一个内核模块,它实现了一个/dev/kvm的字符设备来与用户进行交互,通过调用一系列ioctl函数可以实现qemu和kvm之间的切换.当要创建一个新的虚拟机时,首先打开/dev/kvm设备,在其上调用ioctl函数:

[cpp] view plaincopy

 

  1. system_fd = open("/dev/kvm", ORDWR);  
  2. vm_fd = ioctl(system_fd, KVM_CREATE_VM, 0);  

ioctl函数在kvm中的实现为virt/kvm/kvm_main.c中kvm_dev_ioctl函数,当传入的参数为KVM_CREATE_VM时,该函数会创建一个VM,并且返回一个fd,通过该fd可以操作虚拟机.

 

    创建完虚拟机之后,需要在该虚拟机上面创建vcpu,调用的接口也是ioctl,只是此时对应的fd为创建虚拟机时返回的fd.

[cpp] view plaincopy

 

  1. vcpu_fd = ioctl(vm_fd, VM_CREATE_VCPU, 0)  

此时ioctl函数对应的实现为virt/kvm/kvm_main.c中kvm_vm_ioctl函数,当传入的参数为VM_CREATE_VCPU时,与KVM_CREATE_VM过程类似,它创建一个vcpu并且返回可以操作该vcpu的fd.

 

    创建完vcpu后,可以在该vcpu上面调用ioctl函数进入guest vm.

[cpp] view plaincopy

 

  1. ret = ioctl(vcpu_fd, KVM_RUN, 0);  

此时ioctl函数对应的实现为virt/kvm/kvm_main.c中kvm_vcpu_ioctl函数,若传入的参数为KVM_RUN,它最终会调用vcpu_enter_guest函数进入guest vm.

 

 

    qemu作为一个user mode的程序,其入口为main函数,该main函数定义在vl.c文件中.main函数比较长,其中跟KVM初始化相关的主要有两个函数:configure_accelerator()和machine->init(&args). cofigure_accelerator()函数选择运用哪一种虚拟化方案,其应用到的数据结构为accel_list,会调用accel_list[i].init函数.accel_list的初始化如下所示,当使用kvm虚拟化解决方案时,accel_list[i].init对应的函数即为kvm_init.

[cpp] view plaincopy

 

  1. static struct {  
  2.     const char *opt_name;  
  3.     const char *name;  
  4.     int (*available)(void);  
  5.     int (*init)(void);  
  6.     bool *allowed;  
  7. } accel_list[] = {  
  8.     { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },  
  9.     { "xen", "Xen", xen_available, xen_init, &xen_allowed },  
  10.     { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },  
  11.     { "qtest", "QTest", qtest_available, qtest_init, &qtest_allowed },  
  12. };  

kvm_init函数定义在kvm-all.c文件中,其主要功能是打开/dev/kvm设备,创建一个虚拟机.

 

    machine->init(&arg)函数主要初始化硬件设备,并且调用qemu_init_vcpu为每一个vcpu创建一个线程,线程执行的函数为qemu_kvm_cpu_thread_fn.从qemu main到qemu_init_vcpu之间函数调用关系涉及到一些函数指针的赋值源码比较难于读懂,以下是使用gdb调试打出其调用关系.

[cpp] view plaincopy

 

  1. #0 qemu_init_vcpu (cpu=0x55555681ea90) at /home/dashu/kvm/qemu/qemu-dev-zwu/cpus.c:1084  
  2. #1 0x0000555555909f1e in x86_cpu_realizefn (dev=0x55555681ea90, errp=0x7fffffffd8f8) at /home/dashu/kvm/qemu/qemu-dev-zwu/target-i386/cpu.c:2399  
  3. #2 0x00005555556c768a in device_set_realized (obj=0x55555681ea90, value=true, err=0x7fffffffda88) at hw/core/qdev.c:699  
  4. #3 0x000055555580b93f in property_set_bool (obj=0x55555681ea90, v=0x5555565bab20, opaque=0x5555565375a0, name=0x555555a01f88 "realized", errp=0x7fffffffda88) at qom/object.c:1300  
  5. #4 0x000055555580a484 in object_property_set (obj=0x55555681ea90, v=0x5555565bab20, name=0x555555a01f88 "realized", errp=0x7fffffffda88) at qom/object.c:788  
  6. #5 0x000055555580bbea in object_property_set_qobject (obj=0x55555681ea90, value=0x555556403e40, name=0x555555a01f88 "realized", errp=0x7fffffffda88) at qom/qom-qobject.c:24  
  7. #6 0x000055555580a770 in object_property_set_bool (obj=0x55555681ea90, value=true, name=0x555555a01f88 "realized", errp=0x7fffffffda88) at qom/object.c:851  
  8. #7 0x00005555558a7de0 in pc_new_cpu (cpu_model=0x555555a0200b "qemu64", apic_id=0, icc_bridge=0x55555655b2c0, errp=0x7fffffffdac8) at /home/dashu/kvm/qemu/qemu-dev-zwu/hw/i386/pc.c:922  
  9. #8 0x00005555558a7fed in pc_cpus_init (cpu_model=0x555555a0200b "qemu64", icc_bridge=0x55555655b2c0) at /home/dashu/kvm/qemu/qemu-dev-zwu/hw/i386/pc.c:978  
  10. #9 0x00005555558a923b in pc_init1 (system_memory=0x5555562a7240, system_io=0x5555562a7f60, ram_size=1073741824, boot_device=0x555555a0248a "cad", kernel_filename=0x0, kernel_cmdline=0x5555559f85be "",   
  11. initrd_filename=0x0, cpu_model=0x0, pci_enabled=1, kvmclock_enabled=1) at /home/dashu/kvm/qemu/qemu-dev-zwu/hw/i386/pc_piix.c:105  
  12. #10 0x00005555558a9a36 in pc_init_pci (args=0x7fffffffdf10) at /home/dashu/kvm/qemu/qemu-dev-zwu/hw/i386/pc_piix.c:245  
  13. #11 0x00005555558a9a7f in pc_init_pci_1_6 (args=0x7fffffffdf10) at /home/dashu/kvm/qemu/qemu-dev-zwu/hw/i386/pc_piix.c:255  
  14. #12 0x00005555558584fe in main (argc=10, argv=0x7fffffffe148, envp=0x7fffffffe1a0) at vl.c:4317  

 

 

    qemu_kvm_cpu_thread_fn函数创建vcpu,然后调用kvm_cpu_exec函数.kvm_cpu_exec函数调用ioctl进入kvm并最终进入guest vm.

    以上即为qemu调用kvm的接口初始化kvm的过程.后续我会整理出IO在kvm和qemu之间执行过程,同时描述kvm和qemu之间如何协同工作的.

 

参考资料:

1. qemu-kvm的初始化与客户系统的执行:http://blog.csdn.net/lux_veritas/article/details/9383643

2. 内核虚拟化kvm/qemu----guest os,kvm,qemu工作流程:http://www.360doc.com/content/12/0619/13/7982302_219186951.shtml

 转载:http://blog.csdn.net/dashulu/article/details/17074675

时间: 2024-10-11 22:45:38

KVM的初始化过程的相关文章

Cisco交换机初始化过程

公司有几台交换机需要初始化配置,这里帖出过程,给大家参考下.此初始化过程只测试过Cisco 2960和3550系列交换机,方法可用.感谢测试过程中对我提出帮助的 小侠唐在飞. 一:知道交换机特权密码的情况下 如果你知道交换机的特权密码,那初始化就相当easy了. 1.交换机加电 2.加电完成,输入 enable,进入特权模式 3.执行 erase  startconfig 命令,初始化交换机 4.执行 reload 命令,使交换机重新读取启动文件 5.重启完成,交换机恢复初始化.

Linux系统和内核初始化过程简介

全部引导过程是四步 1:boot PROM phase 2:boot Programs phase 3:kernel initialization phase 4:init phase system初始化,检测内存和cpu,检查设备和创建设备树,设置console kernel初始化过程 kernel self -initialization 内核自检 loading of kernel modules 载入内核模块 reading of the kernel configuration fil

JAVA中对象创建和初始化过程

分析一下JAVA中对象创建和初始化过程中涉及的相关概念问题,java中栈(stack)与堆(heap),对象.引用.句柄的概念. 1.Java中的数据类型 Java中有3个数据类型: 基本数据类型(在Java中,boolean.byte.short.int.long.char.float.double这八种是基本数据类型) 引用类型 null类型 其中,引用类型包括类类型(含数组).接口类型. 下列语句声明了一些变量: 以下是引用片段: int k ; A a; //a是A数据类型的对象变量名.

Java程序初始化过程详解

觉得Core Java在Java 初始化过程的总体顺序没有讲,只是说了构造器时的顺序,作者似乎认为路径很多,列出来比较混乱.我觉得还是要搞清楚它的过程比较好.所以现在结合我的学习经验写出具体过程: 过程如下: 1.在类的声明里查看有无静态元素(static element, 我姑且这么叫吧),比如: static int x = 1, { //block float sss = 333.3; String str = "hello"; } 或者 比如 static { //(stati

sdk- 接入MM SDK 初始化过程中闪退

问题描述 接入MM SDK 初始化过程中闪退 我把SDK中的DEMO拿来运行,可以正常运行,但是在assets中加入几个资源文件夹后,(没有修改任何代码)一运行程序就会 崩溃.附图: 资源附件不太好传,感兴趣的朋友留一下QQ或邮箱. 接入的是MM最新的版本3.1.3

class-类的初始化过程不懂,求大神解释

问题描述 类的初始化过程不懂,求大神解释 class Fu { Fu() { super(); show(); return; } void show() { System.out.println("fu show"); } } class Zi extends Fu { int num = 8; Zi() { super(); System.out.println("zi cons run...."+num); return; } void show() { Sy

《Spring技术内幕》——2.3节IoC容器的初始化过程

2.3 IoC容器的初始化过程 简单来说,IoC容器的初始化是由前面介绍的refresh()方法来启动的,这个方法标志着IoC容器的正式启动.具体来说,这个启动包括BeanDefinition的Resouce定位.载入和注册三个基本过程.如果我们了解如何编程式地使用IoC容器,就可以清楚地看到Resource定位和载入过程的接口调用.在下面的内容里,我们将会详细分析这三个过程的实现. 在分析之前,要提醒读者注意的是,Spring把这三个过程分开,并使用不同的模块来完成,如使用相应的Resourc

不可逆的类初始化过程

类的加载过程说复杂很复杂,说简单也简单,说复杂是因为细节很多,比如说今天要说的这个,可能很多人都不了解:说简单,大致都知道类加载有这么几个阶段,loaded->linked->initialized,为了让大家能更轻松地知道我今天说的这个话题,我不详细说类加载的整个过程,改天有时间有精力了我将整个类加载的过程和大家好好说说(PS:我对类加载过程慢慢清晰起来得益于当初在支付宝做cloudengine容器开发的时候,当时引入了标准的osgi,解决类加载的问题几乎是每天的家常便饭,相信大家如果还在使

深入解析Java对象的初始化过程

我们先来看这道面试题: public class Base{ private String baseName = "base"; //构造方法 public Base(){callName();}   //对象方法 public void callName(){ System. out. println(baseName); } //静态内部类    static class Sub extends Base{ //静态内部类的字段 private String baseName =