• qemu源码分析


    参考:http://lists.gnu.org/archive/html/qemu-devel/2011-04/pdfhC5rVdz7U8.pdf

    1. qemu与Bochs的区别:

    1. Bochs

    Bochs和qemu都是以软件仿真为主的虚拟软件,二者的区别何在?

    Bochs完全是以软件的方式对目标程序(OS以及运行在其上的应用程序)进行仿真。Bochs在自己的内部维护着CPU、内存、IO设备的数据结构,每当Bochs仿真一条指令,就会按照这条指令在真实硬件上运行时应当产生的效果,对这些软件维护硬件数据结构产生相应的影响。

    这种逐条处理的方式,可以保持与真实运行时完全相同的粒度,便于学习和调试。但是由于这是一种一对多的映射方式,即一条机制指令,会被解释成N条指令执行,因此效率的下降是在所难免。

    2. qemu

    qemu采取的是另外一种粒度的仿真。

    qemu会从目标程序中,截取当前需要运行的一段代码(被称作Translation Block),将这段代码先翻译成中间语言(Intermediate Code),再将中间语言翻译成主机体系相关的二进制代码。

    由于Translation Block的粒度大于单条机制指令的粒度,qemu相当于是batch处理指令的仿真操作的,因此会比逐条处理的Bochs性能上快一些。

    除此之外,qemu还会优化对于Translation Block的缓存,以及将多个连接执行的Translation Block链接起来在同一批次进行处理;这两种方式对于反复执行的代码段的仿真性能有很大的提升。

    3. 总结

    简而言之,Bochs适合用于学习,以及比较简单的任务处理,Bochs自带的调试器也很给力,用Bochs调试Linux内核是不错的选择(可以参考:http://www.cnblogs.com/long123king/p/3559816.html等等),但是Bochs不适合用于真实地仿真大型的操作系统,比如Windows,基本上无法做到。

    qemu由于处理方式上有优化,不像Bochs那样可以“原汁原味”地展现指令级别的执行过程,因此不太适合于学习;但是由于qemu性能上的提升,还可以配合内核虚拟化模块kvm,甚至xen,因此qemu可以像主流的虚拟桌面软件(VirtualBox, Vmware等等)一样流畅地运行多种操作系统。如果你需要在Linux上面虚拟化Windows,肯定是qemu更加适合一些。

    tb_find_fast: 查找下一个TB(Translation Block),并且生成主机代码;

    tcg_qemu_tb_exec:执行生成的主机代码,主机代码由三部分组成:

    2. qemu的处理流程

    qemu的仿真主循环位于cpu-exec.c:cpu_exec函数中

    for(;;)
    {
    ......
    tb = tb_find_fast(env);
    ......
    next_tb = cpu_tb_exec(cpu, tc_ptr);
    ......
    }

    1. tb_find_fast:

    用来准备Translation Block;如果缓存中已经准备好的Translation Block,就直接返回;否则调用tb_find_slow函数来构造一个新的TB。

    tb_find_fast
        |
    tb_find_slow
        |
    tb_gen_code
        |
    cpu_gen_code
        |
    gen_intermediate_code 【Guest Code --> tcg op(中间代码)】
        |
    tcg_gen_code【tcg op(中间代码) --> Host Code】
     

    其中,gen_intermediate_code是与体系相关的函数实现,x86的实现位于target-i386/translate.c中,内部调用disas_insn逐条指令处理。

    而tcg_gen_code会调用tcg_gen_code_common,从TB中取出中间代码,将其转换成主机代码。

    2. cpu_tb_exec:

    用来执行生成好的TB。

    cpu_tb_exec
        |
    tcg_qemu_tb_exec
    #define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM (*)(void *))code_gen_prologue)(tb_ptr)

    prologue和epilogue是compiler在生成目标代码时,对函数栈帧的保存与恢复的代码,我们信手拈来一个例子

    objdump -d vl.o

    下面代码中红色的部分就分别是函数的prologue和epilogue。

    000000000000013a <bitmap_empty>:
         13a:       55                      push   %rbp
         13b:       48 89 e5                mov    %rsp,%rbp
         13e:       53                      push   %rbx
         13f:       48 83 ec 28             sub    $0x28,%rsp
         143:       48 89 7d d8             mov    %rdi,-0x28(%rbp)
         147:       89 75 d4                mov    %esi,-0x2c(%rbp)
         14a:       64 48 8b 04 25 28 00    mov    %fs:0x28,%rax
         151:       00 00 
         153:       48 89 45 e8             mov    %rax,-0x18(%rbp)
         157:       31 c0                   xor    %eax,%eax
         159:       8b 45 d4                mov    -0x2c(%rbp),%eax
         15c:       83 f8 40                cmp    $0x40,%eax
         15f:       77 42                   ja     1a3 <bitmap_empty+0x69>
         161:       48 8b 45 d8             mov    -0x28(%rbp),%rax
         165:       48 8b 10                mov    (%rax),%rdx
         168:       8b 45 d4                mov    -0x2c(%rbp),%eax
         16b:       48 98                   cltq   
         16d:       83 e0 3f                and    $0x3f,%eax
         170:       48 85 c0                test   %rax,%rax
         173:       74 19                   je     18e <bitmap_empty+0x54>
         175:       8b 45 d4                mov    -0x2c(%rbp),%eax
         178:       83 e0 3f                and    $0x3f,%eax
         17b:       be 01 00 00 00          mov    $0x1,%esi
         180:       89 c1                   mov    %eax,%ecx
         182:       48 d3 e6                shl    %cl,%rsi
         185:       48 89 f0                mov    %rsi,%rax
         188:       48 83 e8 01             sub    $0x1,%rax
         18c:       eb 07                   jmp    195 <bitmap_empty+0x5b>
         18e:       48 c7 c0 ff ff ff ff    mov    $0xffffffffffffffff,%rax
         195:       48 21 d0                and    %rdx,%rax
         198:       48 85 c0                test   %rax,%rax
         19b:       0f 94 c0                sete   %al
         19e:       0f b6 c0                movzbl %al,%eax
         1a1:       eb 11                   jmp    1b4 <bitmap_empty+0x7a>
         1a3:       8b 55 d4                mov    -0x2c(%rbp),%edx
         1a6:       48 8b 45 d8             mov    -0x28(%rbp),%rax
         1aa:       89 d6                   mov    %edx,%esi
         1ac:       48 89 c7                mov    %rax,%rdi
         1af:       e8 00 00 00 00          callq  1b4 <bitmap_empty+0x7a>
         1b4:       48 8b 5d e8             mov    -0x18(%rbp),%rbx
         1b8:       64 48 33 1c 25 28 00    xor    %fs:0x28,%rbx
         1bf:       00 00 
         1c1:       74 05                   je     1c8 <bitmap_empty+0x8e>
         1c3:       e8 00 00 00 00          callq  1c8 <bitmap_empty+0x8e>
         1c8:       48 83 c4 28             add    $0x28,%rsp
         1cc:       5b                      pop    %rbx
         1cd:       5d                      pop    %rbp
         1ce:       c3                      retq  

    qemu的执行流程中,本来属于qemu的代码,我们可以称之为static code;而通过TB生成的主机代码,我们可以称之为dynamic code,因此必定要有一个入口点,让static code将dynamic code调用起来。qemu采用的是类似函数prologue的方式,这也是为什么我们会看到code_gen_prologue的原因。

    code_gen_prologure指向的是TB中动态生成的相对于整个TB的prologue。

     
  • 相关阅读:
    Oracle 查询表空间容量脚本
    C#保留小数位的方法集合
    asp.net c# 去掉字符串中重复项并将结果遍历出来算法
    汇总sql server数据库所有表名、列数、行数
    Web表单设计之注册表单
    精简高效的CSS命名准则和方法
    MSSQL查看和解除表锁
    XML通用操作类
    Ajax学习笔记一(xmlHttpRequest对象)
    SQL Server无法生成FRunCM线程|FRunCM 线程|FRunCM
  • 原文地址:https://www.cnblogs.com/long123king/p/3584053.html
Copyright © 2020-2023  润新知