设为首页 收藏本站
查看: 1161|回复: 0

[经验分享] [原] KVM 虚拟化原理探究(2)

[复制链接]

尚未签到

发表于 2017-6-24 07:35:12 | 显示全部楼层 |阅读模式
#include <err.h>  #include <fcntl.h>
  #include <linux/kvm.h>
  #include <stdint.h>
  #include <stdio.h>
  #include <stdlib.h>
  #include <string.h>
  #include <sys/ioctl.h>
  #include <sys/mman.h>
  #include <sys/stat.h>
  #include <sys/types.h>
  int main(void)
  {
  int kvm, vmfd, vcpufd, ret;
  const uint8_t code[] = {
  0xba, 0xf8, 0x03, /* mov $0x3f8, %dx */
  0x00, 0xd8,       /* add %bl, %al */
  0x04, '0',        /* add $'0', %al */
  0xee,             /* out %al, (%dx) */
  0xb0, '\n',       /* mov $'\n', %al */
  0xee,             /* out %al, (%dx) */
  0xf4,             /* hlt */
  };
  uint8_t *mem;
  struct kvm_sregs sregs;
  size_t mmap_size;
  struct kvm_run *run;
  // 获取 kvm 句柄
  kvm = open(&quot;/dev/kvm&quot;, O_RDWR | O_CLOEXEC);
  if (kvm == -1)
  err(1, &quot;/dev/kvm&quot;);
  // 确保是正确的 API 版本
  ret = ioctl(kvm, KVM_GET_API_VERSION, NULL);
  if (ret == -1)
  err(1, &quot;KVM_GET_API_VERSION&quot;);
  if (ret != 12)
  errx(1, &quot;KVM_GET_API_VERSION %d, expected 12&quot;, ret);
  // 创建一虚拟机
  vmfd = ioctl(kvm, KVM_CREATE_VM, (unsigned long)0);
  if (vmfd == -1)
  err(1, &quot;KVM_CREATE_VM&quot;);
  // 为这个虚拟机申请内存,并将代码(镜像)加载到虚拟机内存中
  mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
  if (!mem)
  err(1, &quot;allocating guest memory&quot;);

  memcpy(mem, code,>  // 为什么从 0x1000 开始呢,因为页表空间的前4K是留给页表目录
  struct kvm_userspace_memory_region region = {
  .slot = 0,
  .guest_phys_addr = 0x1000,
  .memory_size = 0x1000,
  .userspace_addr = (uint64_t)mem,
  };
  // 设置 KVM 的内存区域
  ret = ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, &region);
  if (ret == -1)
  err(1, &quot;KVM_SET_USER_MEMORY_REGION&quot;);
  // 创建虚拟CPU
  vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long)0);
  if (vcpufd == -1)
  err(1, &quot;KVM_CREATE_VCPU&quot;);
  // 获取 KVM 运行时结构的大小
  ret = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL);
  if (ret == -1)
  err(1, &quot;KVM_GET_VCPU_MMAP_SIZE&quot;);
  mmap_size = ret;

  if (mmap_size <>  errx(1, &quot;KVM_GET_VCPU_MMAP_SIZE unexpectedly small&quot;);
  // 将 kvm run 与 vcpu 做关联,这样能够获取到kvm的运行时信息
  run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0);
  if (!run)
  err(1, &quot;mmap vcpu&quot;);
  // 获取特殊寄存器
  ret = ioctl(vcpufd, KVM_GET_SREGS, &sregs);
  if (ret == -1)
  err(1, &quot;KVM_GET_SREGS&quot;);
  // 设置代码段为从地址0处开始,我们的代码被加载到了0x0000的起始位置
  sregs.cs.base = 0;
  sregs.cs.selector = 0;
  // KVM_SET_SREGS 设置特殊寄存器
  ret = ioctl(vcpufd, KVM_SET_SREGS, &sregs);
  if (ret == -1)
  err(1, &quot;KVM_SET_SREGS&quot;);
  // 设置代码的入口地址,相当于32位main函数的地址,这里16位汇编都是由0x1000处开始。
  // 如果是正式的镜像,那么rip的值应该是类似引导扇区加载进来的指令
  struct kvm_regs regs = {
  .rip = 0x1000,
  .rax = 2,    // 设置 ax 寄存器初始值为 2
  .rbx = 2,    // 同理
  .rflags = 0x2,   // 初始化flags寄存器,x86架构下需要设置,否则会粗错
  };
  ret = ioctl(vcpufd, KVM_SET_REGS, &regs);
  if (ret == -1)
  err(1, &quot;KVM_SET_REGS&quot;);
  // 开始运行虚拟机,如果是qemu-kvm,会用一个线程来执行这个vCPU,并加载指令
  while (1) {
  // 开始运行虚拟机
  ret = ioctl(vcpufd, KVM_RUN, NULL);
  if (ret == -1)
  err(1, &quot;KVM_RUN&quot;);
  // 获取虚拟机退出原因
  switch (run->exit_reason) {
  case KVM_EXIT_HLT:
  puts(&quot;KVM_EXIT_HLT&quot;);
  return 0;
  // 汇编调用了 out 指令,vmx 模式下不允许执行这个操作,所以
  // 将操作权切换到了宿主机,切换的时候会将上下文保存到VMCS寄存器
  // 后面CPU虚拟化会讲到这部分
  // 因为虚拟机的内存宿主机能够直接读取到,所以直接在宿主机上获取到
  // 虚拟机的输出(out指令),这也是后面PCI设备虚拟化的一个基础,DMA模式的PCI设备
  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, &quot;unhandled KVM_EXIT_IO&quot;);
  break;
  case KVM_EXIT_FAIL_ENTRY:
  errx(1, &quot;KVM_EXIT_FAIL_ENTRY: hardware_entry_failure_reason = 0x%llx&quot;,
  (unsigned long long)run->fail_entry.hardware_entry_failure_reason);
  case KVM_EXIT_INTERNAL_ERROR:
  errx(1, &quot;KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x&quot;, run->internal.suberror);
  default:
  errx(1, &quot;exit_reason = 0x%x&quot;, run->exit_reason);
  }
  }
  }

运维网声明 1、欢迎大家加入本站运维交流群:群②:261659950 群⑤:202807635 群⑦870801961 群⑧679858003
2、本站所有主题由该帖子作者发表,该帖子作者与运维网享有帖子相关版权
3、所有作品的著作权均归原作者享有,请您和我们一样尊重他人的著作权等合法权益。如果您对作品感到满意,请购买正版
4、禁止制作、复制、发布和传播具有反动、淫秽、色情、暴力、凶杀等内容的信息,一经发现立即删除。若您因此触犯法律,一切后果自负,我们对此不承担任何责任
5、所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其内容的准确性、可靠性、正当性、安全性、合法性等负责,亦不承担任何法律责任
6、所有作品仅供您个人学习、研究或欣赏,不得用于商业或者其他用途,否则,一切后果均由您自己承担,我们对此不承担任何法律责任
7、如涉及侵犯版权等问题,请您及时通知我们,我们将立即采取措施予以解决
8、联系人Email:admin@iyunv.com 网址:www.yunweiku.com

所有资源均系网友上传或者通过网络收集,我们仅提供一个展示、介绍、观摩学习的平台,我们不对其承担任何法律责任,如涉及侵犯版权等问题,请您及时通知我们,我们将立即处理,联系人Email:kefu@iyunv.com,QQ:1061981298 本贴地址:https://www.yunweiku.com/thread-387453-1-1.html 上篇帖子: kvm虚拟化管理平台WebVirtMgr部署-完整记录(3) 下篇帖子: linux命令行安装使用KVM
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

扫码加入运维网微信交流群X

扫码加入运维网微信交流群

扫描二维码加入运维网微信交流群,最新一手资源尽在官方微信交流群!快快加入我们吧...

扫描微信二维码查看详情

客服E-mail:kefu@iyunv.com 客服QQ:1061981298


QQ群⑦:运维网交流群⑦ QQ群⑧:运维网交流群⑧ k8s群:运维网kubernetes交流群


提醒:禁止发布任何违反国家法律、法规的言论与图片等内容;本站内容均来自个人观点与网络等信息,非本站认同之观点.


本站大部分资源是网友从网上搜集分享而来,其版权均归原作者及其网站所有,我们尊重他人的合法权益,如有内容侵犯您的合法权益,请及时与我们联系进行核实删除!



合作伙伴: 青云cloud

快速回复 返回顶部 返回列表