Chinaunix首页 | 论坛 | 博客
  • 博客访问: 213599
  • 博文数量: 76
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 513
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-23 00:06
个人简介

展示自己、证明自己

文章分类

全部博文(76)

文章存档

2018年(1)

2014年(55)

2013年(20)

我的朋友

分类: C/C++

2014-07-07 10:36:31

转载:http://blog.csdn.net/lux_veritas/article/details/9383643

---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------


虚拟机运行概览

首先直观的了解一下利用QEMU运行客户虚拟机的流程。

在命令行中运行QEMU的系统模式的可执行文件,参数声明虚拟CPU的个数,内存大小,指定已经安装好的硬盘镜像,启动QEMU虚拟机主窗口。启动命令格式举例:

qemu-system-x86_64 --enable-kvm -cpu host \ 
-smp cores=4,threads=2,sockets=4 \
-m 16384 -k en-us -hda /pps/guohongwei/vm_test/ubuntu.img -monitor stdio

以下为笔者的Mac-mini上弹出QEMU虚拟机界面的图示:







QEMU的核心初始化流程

客户系统运行之前,QEMU作为全系统模拟软件,需要为客户系统模拟出CPU、主存以及I/O设备,使客户系统就像运行在真实硬件之上,而不用对客户系统代码做修改。

如 概览部分所示,由用户为客户系统指定需要的虚拟CPU资源(包括CPU核心数,SOCKET数目,每核心的超线程数,是否开启NUMA等等),虚拟内存资 源,具体参数设置参见${QEMU}/qemu-options.hx。创建QEMU主线程,执行QEMU系统的初始化,在初始化的过程中针对每一个虚拟 CPU,单独创建一个posix线程。每当一个虚拟CPU线程被调度到物理CPU上执行时,该VCPU对应的一套完整的寄存器集合被加载到物理CPU上, 通过VM-LAUNCH或VM-RESUME指令切换到非根模式执行。直到该线程时间片到,或者其它中断引发虚拟机退出,VCPU退出到根模式,进行异常 处理。

如下图所示,当用户运行QEMU的System Mode的可执行文件时,QEMU从${QEMU}/vl.c的main函数执行主线程。以下着重分析,客户系统启动之前,QEMU所做的初始化工作:





1.处理命令行参数:

进入vl.c的main函数,首先有一个很长的for(;;)循环,用于分析处理通过命令行传进来的参数,进行相应系统的初始化设置。比如创建多少VCPU,是否开启NUMA,分配多少虚拟内存资源等等。


2.选择虚拟化方案:

configure_accelerator()函数,选择使用哪种虚拟化解决方案。
accel_list[] = {
    { "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },
    { "xen", "Xen", xen_available, xen_init, &xen_allowed },
    { "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },
    { "qtest", "QTest", qtest_available, qtest_init, &qtest_allowed },
};

accel_list[]数组声明了QEMU使用的系统模拟方案。“tcg”模式是不 使用任何硬件虚拟化辅助方式,采用基于二进制指令翻译的方式,将目标平台的指令代码通过一个叫做TCG的模块翻译为本机可以执行的指令。“xen”、 “kvm”分别为两种主流的开源虚拟化解决方案。本文主要针对kvm这种硬件辅助的虚拟化解决方案。




3.初始化内存布局:

新版本的QEMU(1.4)中,cpu_exec_init_all()函数只负责注册主存与IO内存两个顶层的memory_region,并且注册memory_listener。


 



4.虚拟客户机硬件初始化:

在完成了QEMU自身的初始化工作后,便开始了客户系统核心的初始化工作,主要是 QEMU根据命令行参数,为客户系统创建虚拟的CPU、内存、I/O资源。核心过程是machine->init(&args),对于 x86目标平台,实际调用的是pc_init1函数。下面着重分析该函数。




4.1 VCPU初始化pc_cpus_init()

void pc_cpus_init(const char *cpu_model)
{
    int i;


    /* init CPUs */
    if (cpu_model == NULL) {
#ifdef TARGET_X86_64
        cpu_model = "qemu64";
#else
        cpu_model = "qemu32";
#endif
    }


    for (i = 0; i < smp_cpus; i++) {
        if (!cpu_x86_init(cpu_model)) {
            fprintf(stderr, "Unable to find x86 CPU definition\n");
            exit(1);
        }
    }
}

pc_init1的函数调用关系如下图所示,对于每一个即将创建的VCPU(个数由命令行传 入smp_cpus),执行cpu_x86_init,逐层调用后,由qemu_kvm_start_vcpu创建一个VCPU线程,新的VCPU线程将 执行qemu_kvm_cpu_thread_fn函数,逐层调用后经过kvm_vcpu_ioctl系统调用切换到核心态,由KVM执行VCPU的创建 工作,包括创建VMCS等非根模式下工作所需要的核心数据结构。



 

4.2 pc_memory_init初始化主存空间

利用mmap系统调用,在QEMU主线程的虚拟地址空间中申明一段连续的大小的空间用 于客户机物理内存映射。在QEMU的内存管理结构中逐步添加subregion。此处添加了低于4g的memory_region,高于4g的 memory_region,BIOS的memory_region。并调用bochs_bios_init,初始化BIOS。



 
4.3 i440fx_init初始化桥片及总线结构
4.4 pc_cmos_init初始化CMOS及时钟



客户系统的执行流程

在创建了全部VCPU后,这些VCPU线程并没有被立即调度执行,直至vl.c的 main函数执行完全部初始化工作后,调用resume_all_vcpus(),将pcpu->stop和 pcpu->stopped 置为false,当VCPU线程再次被调度到物理CPU上执行时,VCPU正式开始工作。

由于pc_memory_init阶段已将BIOS初始化,创建了“bios.bin”文件到内存的映射,已处于非根模式下的VCPU到客户物理地址0xFFFF0(即“bios.bin”文件被映射到的地址的一个线性偏移)处,获取第一条指令,开始执行客户系统代码。

此时多个VCPU线程由宿主系统轮流调度执行,QEMU主线程处于循环中,用于接收来 自客户系统传回的I/O模拟请求。每一个VCPU线程,只要被调度的物理CPU上,便切到非根模式执行客户系统代码,产生需要退出的异常时(如EPT缺 页,处理I/O指令等),保存异常原因到VMCS,切换到根模式下,由KVM捕获该异常,查询VMCS异常号执行相应处理,完成后再切换回非根模式,如此 循环往复执行下去。




阅读(638) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~