QEMU 与KVM概述

2022-01-26  本文已影响0人  川人1588

虚拟化简介

虚拟化思想

虚拟化的主要思想是,通过分层将底层的复杂、难用的资源虚拟抽象成简单、易用的资源,提供给上层使用。如:

  1. 计算机进程对计算资源进行抽象,每个进程都认为自己独占整个计算及系统资源。
  2. TCP/IP协议将网卡设备进行虚拟化,应用只需要关心地址和端口即可。

虚拟机简介

  1. 进程可以理解为一个虚拟机,有自己独立的地址空间和独立的CPU和寄存器;
  2. 模拟器是另一种形式的虚拟机,可以是一种硬件指令集(instruction set architecture,ISA)编译的程序运行在另一种目标ISA上,一般有两种方式实现:
    1. 二进制翻译
    2. 解释方式实现
  3. 高级语言虚拟机;
  4. 系统虚拟机

系统虚拟化的历史

虚拟化技术编年史.jpeg

硬件虚拟化特性

QEMU 与KVM架构

QEMU

  1. 完成用户程序模拟:将一个平台编译的二进制文件运行在另一个不同的平台,如一个arm指令集的二进制,通过QEMU的TCG引擎的处理之后,ARM指令被转换成TCG中间代码,让后在转换为目的平台代码。
  2. 系统虚拟化模拟 :模拟一个完整的系统虚拟机,支持X86,ARM,MIPS,PPC等,早期通过TCG完成各种硬件平台模拟。

KVM

  1. kvm本身为一个内核模块,导出了一系列的接口到用户空间;
  2. 最开始kvm只负责最核心的CPU和内存虚拟化部分,使用QEMU作为其用户态组件,负责完成大量外设的模拟。

QEMU与KVM架构

qemu 与kvm整体架构图

cpu虚拟化

内存虚拟化

设备虚拟化

  1. QEMU模拟
  2. virtIO
  3. 设备直通
  4. SR-IOV(单根输入输出虚拟化):硬件虚拟化

中断虚拟化

  1. Intel 8259 支持单CPU
  2. I/O APIC:I/O advanced programmable interrupt controller
  3. LAPIC:local advanced programmable interrupt controller

KVM接口

  1. 全局ioctl接口
  2. 虚拟机相关ioctl接口
  3. vCPU相关ioctl接口

调用kvm接口示例

#include <stdio.h>
#include <stdlib.h>
#include <linux/kvm.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <stdint.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>

#define err_exit(x) do{perror((x));return 1;}while(0)

int main(int argc, char *argv[]) {
    int kvm, vmfd, vcpufd, ret;
    /* 输出al+bl的值 */
    const uint8_t code[] = {
        0xba, 0xf8, 0x03, /* mov $0x3f8,%dx */
        0x00, 0xd8,  /* add %bl,%al */
        0x04, '0',   /* add $0x30,%al */
        0xee,        /* out %al,(%dx) */
        0xb0, '\n',  /* mov $0x0a,%al */
        0xee,        /* out %al,(%dx) */
        0xf4,        /* hlt */
    };
    uint8_t *mem;
    struct kvm_sregs sregs;
    size_t mmap_size;
    struct kvm_run *run;

    kvm = open("/dev/kvm", O_RDWR|O_CLOEXEC);
    if (kvm==-1) {
        err_exit("Open /dev/kvm failed!\n");
    }

    ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
    if (ret==-1) {
        err_exit("KVM_GET_API_VERSION");
    } else if(ret!=12) {
        err_exit("KVM_GET_API_VERSION not 12");
    }

    /* 检查是否存在KVM_CAP_USER_MEMORY extension */
    ret = ioctl(kvm, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);
    if (ret==-1) {
        err_exit("KVM_CHECK_EXTENSION");
    }
    if (!ret) {
        err_exit("Required extension KVM_CAP_USER_MEM not available");
    }

    /* 创建虚拟机 */
    vmfd = ioctl(kvm, KVM_CREATE_VM, (unsigned long)0);
    if (vmfd==-1) {
        err_exit("KVM_CREATE_VM");
    }

    /* 申请4096字节内存 */
    mem = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0);
    if (!mem) {
        err_exit("allocation guest memory");
    }
    /* 将测试代码复制到这块内存 */
    memcpy(mem, code, sizeof(code));

    struct kvm_userspace_memory_region region= {
        .slot = 0,
        .guest_phys_addr= 0x1000,       //虚拟机物理内存空间的位置
        .memory_size = 0x1000,            //物理空间的大小
        .userspace_addr = (uint64_t)mem,    //在宿主机上虚拟空间的地址
    };
    /* 通知VM内存区域,设置内存地址 */
    ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
    if (ret==-1) {
        err_exit("KVM_SET_USER_MEMORY_REGION");
    }

    /* 创建VCPU */
    vcpufd= ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
    if (vcpufd==-1) {
        err_exit("KVM_CREATE_VCPU");
    }
    /* 获取可映射的内存的大小 */
    ret = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); //获取kvm_run结构体大小
    if (ret==-1) {
        err_exit("KVM_GET_VCPU_MMAP_SIZE");
    }

    mmap_size = ret;
    if (mmap_size<sizeof(*run)) {
        err_exit("KVM_GET_VCPU_MMAP_SIZE unexpectedly small");
    }

    /* 将kvm_run结构体映射到用户空间 */
    run = mmap(NULL, mmap_size, PROT_READ|PROT_WRITE, MAP_SHARED, vcpufd, 0);
    if (!run) {
        err_exit("mmap vcpu");
    }

    /* 设置cs寄存器 */
    ret = ioctl(vcpufd, KVM_GET_SREGS, &sregs);
    if (ret==-1) {
        err_exit("KVM_GET_SREGS");
    }
    sregs.cs.base = 0;
    sregs.cs.selector = 0;
    ret = ioctl(vcpufd, KVM_SET_SREGS, &sregs);
    if (ret==-1) {
        err_exit("KVM_SET_SREGS");
    }

    struct kvm_regs regs = {
        .rip = 0x1000,
        .rax = 2,
        .rbx = 2,
        .rflags = 0x2,
    };
    ret = ioctl(vcpufd, KVM_SET_REGS, &regs);
    if (ret==-1) {
        err_exit("KVM_SET_REGS");
    }

    while(1) {
        //vm entry, 进入non root 模式运行
        ret = ioctl(vcpufd, KVM_RUN, NULL);
        if (ret==-1) {
            err_exit("KVM_RUN");
        }
        switch (run->exit_reason) {
        case KVM_EXIT_HLT:
            puts("KVM_EXIT_HLT");
            return 0;
        case KVM_EXIT_IO:
            /* 因为IO退出虚机,vm exit陷入kvm,调用qemu处理IO请求,进入guest root模式 ring3*/
            if (run->io.direction==KVM_EXIT_IO_OUT && run->io.size==1 && run->io.port==0x3f8 && run->io.count==1) {
                putchar(*(((char *)run) + run->io.data_offset));    //处理虚拟机IO操作
            } else {
                fprintf(stderr, "unhandled KVM_EXIT_IO\n");
                fprintf(stdout, "KVM_EXIT_IO size:%d port:0x%x count:%d\n", run->io.size, run->io.port, run->io.count);
                return 1;
            }
            break;
        case KVM_EXIT_FAIL_ENTRY:
            fprintf(stderr, "KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason=0x%llx\n",
                    (unsigned long long)run->fail_entry.hardware_entry_failure_reason);
            return 1;
        case KVM_EXIT_INTERNAL_ERROR:
            fprintf(stderr, "KVM_EXIT_INTERNAL_ERROR: suberror=0x%x\n", run->internal.suberror);
            return 1;
        default:
            fprintf(stderr, "exit_reason=0x%x\n", run->exit_reason);
            return 0;
        }
    }
}
上一篇下一篇

猜你喜欢

热点阅读