zoukankan      html  css  js  c++  java
  • kvm虚拟化

    [root@localhost cloud_images]# qemu-system-aarch64 -smp 8 -m 8192 -cpu host -M virt -nographic -drive file=vhuser-test1.qcow2,id=hd0   -device virtio-blk-device,drive=hd0   -device e1000,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22
    qemu-system-aarch64: The 'host' CPU type can only be used with KVM
    [root@localhost cloud_images]# qemu-system-aarch64   -name vm3 -enable-kvm -smp 8 -m 8192 -cpu host -M virt -nographic -drive file=vhuser-test1.qcow2,id=hd0   -device virtio-blk-device,drive=hd0   -device e1000,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22
    qemu-system-aarch64: -drive file=vhuser-test1.qcow2,id=hd0: Drive 'hd0' is already in use because it has been automatically connected to another device (did you need 'if=none' in the drive options?)
    [root@localhost cloud_images]# qemu-system-aarch64   -name vm3 -enable-kvm -smp 8 -m 8192 -cpu host -M virt -nographic -drive file=vhuser-test1.qcow2    -device e1000,netdev=net0 -netdev user,id=net0,hostfwd=tcp::5555-:22

    下面,总结一下qemu-kvm软件的架构特点:

    1、Kvm本身只提供两个内核模块。Kvm实现了vcpu和内存的管理;

    2、Qemu控制逻辑,负责创建虚拟机,创建vcpu等。

    在详细介绍,kvm提供了三种通过不同的io_ctl接口来控制的概念:

    1、struct kvm:代表kvm模块本身,用来管理kvm版本信息,创建一个vm;

    2、struct vm:代表一个虚拟机。通过vm的io_ctl接口,可以为虚拟机创建vcpu,设置内存区间,创建中断控制芯片,分配中断等等;

    3、struct vcpu:代表一个vcpu。通过vcpu的io_ctl接口,可以启动或者暂停vcpu,设置vcpu的寄存器,为vcpu注入中断等等。

    首先,定义一个简单地虚拟机需运行代码:

    
    
    mov $0x3f8, %dx
    add %bl, %al
    add $'0', %al
    out %al, (%dx)
    mov $'
    ', %al
    out %al, (%dx)
    hlt
    
    
    
    
    

    这段代码比较简单,也就是先将al和bl寄存器的值相加(初始默认值均为2),结果转换后,输出至0x3f8端口,最后停机。然后,我们通过gcc和objdump将上述二进制代码转换为机器码,内容如下:

    
    
    constuint8_t code[]={
      0xba,0xf8,0x03,/* mov $0x3f8, %dx */
      0x00,0xd8,/* add %bl, %al */
      0x04,'0',/* add $'0', %al */
      0xee,/* out %al, (%dx) */
      0xb0,'
    ',/* mov $'
    ', %al */
      0xee,/* out %al, (%dx) */
      0xf4,/* hlt */
    };
    
    
    
    
    

    需要指出的是,运行这段代码需要CPU"unrestricted guest"特性支持。

    下面,简略叙述一下QEMU、KVM的交互过程。

    先定义并初始化几个变量:

    
    
    /* 向KVM注册用户态内存空间,也即向虚拟机添加“物理内存” */
      /* 注意该“物理内存”是qemu进程向host申请的用户态内存 */
    struct kvm_userspace_memory_region region = {
      .slot = 0,
      .guest_phys_addr = 0x1000,    ----物理内存
      .memory_size = 0x1000,
      .userspace_addr = (uint64_t)mem,
    };
      /* 通用寄存器信息初始化,此处可以看到a、b寄存器值初始化为2 */
    struct kvm_regs regs = {
      .rip = 0x1000,
      .rax = 2,
      .rbx = 2,
      .rflags = 0x2,
    };
      /* cs段寄存器信息初始化 */
    sregs.cs.base = 0;
    sregs.cs.selector = 0;
    
    
    
    
    

    运行过程如下:

    
    
    void main(){
      /* 打开kvm控制的总设备文件/dev/kvm */
      kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC);
      /* 检查API版本信息,检测ret值 */
      ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
      if (ret == -1)
        err(1, "KVM_GET_API_VERSION");
      if (ret != 12)
        errx(1, "KVM_GET_API_VERSION %d, expected 12", ret);
    /* 创建虚拟机 */ 
      vmfd = ioctl(kvm, KVM_CREATE_VM, (unsigned long)0);
      /* 获取页对齐且初始化为0的一个内存页  0X1000是物理地址*/
      mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
      /* 将前述二进制代码拷贝至该页内 */
      memcpy(mem, code, sizeof(code));
      /* 将二进制页赋予虚拟机 */ 
      /* 此时,虚拟机将其当做物理内存,且只有一个slot */
      ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
      /* 创建VCPU,且每个VCPU关联一个struct kvm_run结构体 */
      vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
      /* 计算需要kernel和用户空间共享的struct kvm_run结构体大小 */
      mmap_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
      /* 执行共享struct kvm_run结构体*/
      run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
      /* 初始化虚拟机的VCPU寄存器信息,准备运行 */
      ioctl(vcpufd, KVM_GET_SREGS, &sregs);
      ioctl(vcpufd, KVM_SET_REGS, &regs);
      /* 运行 */
      while (1) {
        /* 进入运行 */
        ioctl(vcpufd, KVM_RUN, NULL);
        /* 退出处理 */
        switch (run->exit_reason) {
          case KVM_EXIT_HLT:
            puts("KVM_EXIT_HLT");
            return 0;
          case KVM_EXIT_IO:
            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));
            else
              errx(1, "unhandled KVM_EXIT_IO");
            break;
          case KVM_EXIT_FAIL_ENTRY:
            errx(1, "KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx",
             (unsigned long long)run->fail_entry.hardware_entry_failure_reason);
          case KVM_EXIT_INTERNAL_ERROR:
            errx(1, "KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x",
               run->internal.suberror);
          default:printf("error
    "); break;
        }
      }  
    }
    
    
    
    
    

           上面已经简略地注释了各行代码。我们大体上搞明白了创建、加载、运行虚拟机的基本流程。

  • 相关阅读:
    Phonics 自然拼读法 s,a,t,i,p,n Teacher:Lamb
    English Voice of <<City of stars>>
    English trip EM2-LP-1A Hi Teacher:Taylor
    English trip EM2-PE-6B Teacher:Taylor,Patrick(2019.12.2)
    English trip EM2-PE-6A Family Relationship Teacher:Taylor
    keras各种优化方法总结 SGDmomentumnesterov
    keras做DNN
    keras、 tensor flow 教程
    DNN例子
    tensor flow 的两种padding方式
  • 原文地址:https://www.cnblogs.com/dream397/p/13924395.html
Copyright © 2011-2022 走看看