QEMU JIT/KVMQEMU 采用了动态二进制翻译(Dynamic Binary Translation)机制,以一种中间代码作为中介,将客户机架构的机器码运行时翻译(Just-In-Time,JIT)为宿主机架构的机器码形式,并将翻译结果交由宿主机的处理器直接执行。因而 QEMU 在不同的体系架构下也具有不错的性能。QEMU 的这种虚拟化加速方式称为TCG(Tiny Code Generator)。此外,QEMU 还可以使用其他虚拟机管理程序进行加速。其中最常见的是使用KVM(Kernel Virtual Machine)。KVM 是 Linux 的一个内核驱动模块,采用了硬件辅助的虚拟化技术,能够让 Linux 主机成为一个虚拟机管理程序。此时虚拟机管理程序直接位于硬件之上,因此属于第一类虚拟机管理程序。由于不需要经过操作系统,能够直接访问硬件资源,所以 KVM 的性能要优于纯 QEMU。当 QEMU 配合 KVM 使用时,会由 QEMU 负责 I/O 虚拟化,而 KVM 负责处理器和内存虚拟化。这种方法相较于直接使用 QEMU,可以极大提高性能;而相较于直接使用 KVM,又获得了强大的平台能力。因此二者相互配合,能够发挥自身的优势,相得益彰.JIT 在 QEMU 中负责在运行时将目标架构的指令动态翻译为主机架构的指令,并缓存翻译结果以供重用。流程如下:目标架构代码 (如ARM程序) ↓ 运行时翻译 QEMU JIT 编译器 (TCG: Tiny Code Generator) ↓ 生成 主机架构代码 (如x86指令) ↓ 执行 直接在CPU上运行TCG是QEMU 的 JIT 引擎核心,TCG实现有native jit和TCI两种技术,后面会分别介绍。探索环境实验平台信息:首先编译X86版本和ARM版本两个平台的QEMU1.获取代码:wget https://download.qemu.org/qemu-4.1.0.tar.bz22.安装依赖:sudo apt-get install build-essential pkg-config zlib1g-dev libglib2.0-0 libglib2.0-dev libsdl1.2-dev libpixman-1-dev libfdt-dev autoconf automake libtool librbd-dev libaio-dev flex bison libattr1-dev libcap-ng-dev libcap-dev3.编译./configure --target-list=arm-softmmu --audio-drv-list=alsa,pa --prefix=/home/caozilong/Workspace/qemu/install接着执行 make -j44.生成DEB包sudo apt-get install checkinstall sudo checkinstall make install5.安装结果6.分析由于HOST机是X86架构,所以这里的tcg-target.inc.c一定是X86目录下的tcg-target.inc.c文件。ARM平台上:arm平台上会使用arm目录的tcg-target.inc.c原理方面,Qemu采用动态二进制翻译(Binary Translation)技术,能够支持多源(HOST)多目标(target)的跨平台仿真执行异构指令集,如下图所示,在有限的几个基础架构上,支持运行了远远超过基础架构数量的目标ISA架构。总体上的工作模型如下:如何判断target 目录和tcg目录各自的角色,可以根据const TranslatorOps对象的位置判断,此对象注册了对目标架构的指令ISA的翻译(JIT)函数指针,它定义在target目录,自然也就是TARGE目录描述的是被模拟的目标指令架构:也可以通过TCI目录的所在位置判断那个目录是HOST机(源机),因为它是通用目标ISA的解释器。TCI目录在tcg/tci下,自然和tci目录平级的架构目录都是源机目录。helper和TCG的分派TCG翻译引擎KVM加速虚拟化过程虽然执行很快,但是要求目标平台和HOST平台为同一架构才有可能,而TCG引擎没有这个要求,TCG可以将目标机架构的二进制指令翻译为HOST指令,放到一片预先分配好的code_gen_buffer中,code_gen_buffer具有可执行权限,CPU可以执行放在其中的指令,用这种方式实现本地CPU加速。带有可执行属性MAP的CODE_GEN_BUFFER分配过程:可执行匿名区映射的例子:#include unistd.h #include stdio.h #include sys/mman.h #include fcntl.h #include stdlib.h #include errno.h #include string.h #define FILE_SIZE 0x4000 int add(int a, int b) { return a+b; } typedef int (*ptrfunc)(int, int); int main(void) { int i; unsigned int *maddr; ptrfunc ptrf; #if 0 maddr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); #else maddr = mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE | PROT_EXEC, MAP_SHARED|MAP_ANONYMOUS, -1, 0); #endif if(maddr == (void*)-1) { printf("%s line %d, faild to map buffer, error %s.\n", __func__, __LINE__, strerror(errno)); return -1; } printf("%s line %d maddr = %p errno %s.\n", __func__, __LINE__, maddr, strerror(errno)); memcpy(maddr, add, 64); ptrf = (ptrfunc)maddr; printf("%s line %d, maddr = %p, maddr(5,6) = %d.\n", __func__, __LINE__, maddr, ptrf(5, 6)); for(i = 0; i 16; i ++) { maddr[i] = i; printf("0x%02x ", maddr[i]); } printf("\n"); munmap((void *)maddr, FILE_SIZE); return 0; }如果不支持KVM,则可以使能TCI,它会启动一个虚拟机解释器,这样可以兼容所有的架构:当不配置--enable-tcg-interpreter时,TCG的后端是native x86_64.意思是说,将TARGET的指令集翻译为本地的x86_64指令,再交给本地CPU执行。支持TCI的编译选项,当添加--enable-tcg-interpreter支持后,TCG的实现后端就发生了变化,此时的TCG后端成为了TCG with bytecode interpreter,也就是TARGET的指令依赖主机纯解释执行,解释器的效率是很低的,效率肯定打了折扣。$ ./configure --target-list=arm-softmmu,aarch64-softmmu,i386-softmmu,x86_64-softmmu,riscv32-softmmu,riscv64-softmmu,aarch64-linux-user,arm-linux-user,riscv64-linux-user,x86_64-linux-user --audio-drv-list=alsa,sdl,pa --enable-system --enable-user --enable-linux-user --enable-sdl --enable-vnc --enable-virtfs --enable-kvm --enable-fdt --enable-debug --disable-strip --enable-debug-tcg --enable-debug-info --enable-debug --disable-strip --enable-vnc --enable-tcg-interpreter --prefix=/home/zlcao/Workspace/qemu/install/