- 实验环境
- kvm 简易运行
- vmware 与 windows 文件共享
- 编译kvmtool
- 参考
虚拟机:VMware Workstation 16 Player
os: centos7+linux5.10 (自己安装内核)
#include#include #include #include #include #include #include #include #include int main(void) { int kvm, vmfd, vcpufd,ret; uint8_t *mem; struct kvm_sregs sregs; size_t mmap_size; struct kvm_run *run; // 每次执行in/out都会触发VM-Exit,KVM将处理VM-Exit,如果是IO导致的,则KVM将继续向上提交,交由QEMU等用户程序将继续处理 const uint8_t code[] = { 0xba, 0xf8, 0x03, 0xb0, 'K', 0xee, 0xb0, 'V', 0xee, 0xb0, 'M', 0xee, 0xb0, 'n', 0xee, 0xf4, }; // 打开KVM模块设备文件 if((kvm = open("/dev/kvm", O_RDWR | O_CLOEXEC))==-1) { fprintf(stderr,"open kvm Error:%san",strerror(errno)); exit(1); } // kvm 打开的设备文件dev/kvm的文件描述符 if((ret = ioctl(kvm, KVM_GET_API_VERSION, NULL))==-1) { fprintf(stderr,"kvm version Error:%san",strerror(errno)); exit(1); } if (ret != 12) { fprintf(stderr,"KVM_GET_API_VERSION %d, expected 12",ret); exit(1); } // 判断当前kvm 版本是否符合要求 // 创建虚拟机, 并获取其文件描述符 if((vmfd =ioctl(kvm, KVM_CREATE_VM, (unsigned long) 0))==-1) { fprintf(stderr,"kvm create Error:%san",strerror(errno)); exit(1); } // 创建4KB大小的内存空间(一页匿名页),用于存放VM执行的代码 mem = mmap(NULL, 0x1000, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0); // 将上述代码拷贝到匿名页中 memcpy(mem, code, sizeof(code)); // 将代码映射到VM物理内存(GPA)的第二个页框处,因为第一个页框被实模式保留,用于存放IDT(中断向量表) struct kvm_userspace_memory_region region = { .slot = 0, // 内存卡槽 .guest_phys_addr = 0x1000, // GPA的起始映射地址,即第二个页框 .memory_size = 0x1000, // 映射内存的大小,4KB .userspace_addr = (uint64_t) mem, // 映射内存的起始地址 }; if((ret =ioctl(vmfd, KVM_SET_USER_MEMORY_REGION, ®ion))==-1) { fprintf(stderr,"kvm Error:%san",strerror(errno)); exit(1); } // 创建vCPU并获取其文件描述符 if(( vcpufd = ioctl(vmfd, KVM_CREATE_VCPU, (unsigned long) 0))==-1) { fprintf(stderr,"kvm Error:%san",strerror(errno)); exit(1); } // 映射vCPU的共享内存到run中,可以访问其kvm_run结构 mmap_size = ioctl(kvm, KVM_GET_VCPU_MMAP_SIZE, NULL); run = mmap(NULL, mmap_size, PROT_READ | PROT_WRITE, MAP_SHARED, vcpufd, 0); // 设置代码段寄存器和代码段基址, 这是内存的分段管理 // 此时我们并没有开启分页管理,因此虚拟地址直接等于物理地址 // 而虚拟地址是根据线性地址+代码段基址计算的 // 代码段基址是根据GDT(全局描述符表)和CS(代码段,选择子)确定的 if((ret = ioctl(vcpufd, KVM_GET_SREGS, &sregs))==-1) { fprintf(stderr,"kvm Error:%san",strerror(errno)); exit(1); } sregs.cs.base = 0; sregs.cs.selector = 0; if(( ret = ioctl(vcpufd, KVM_SET_SREGS, &sregs))==-1) { fprintf(stderr,"kvm Error:%san",strerror(errno)); exit(1); } // 关于kvm_sregs和kvm_regs(并不严格): // kvm_regs 用于通用寄存器的配置 // kvm_sregs 用于段计算器的配置 // 下用于设置rip寄存器, cs.base + rip 既是物理地址(没开启分页,虚拟地址等于物理地址,rip则表示线性地址), struct kvm_regs regs = { .rip = 0x1000, }; ioctl(vcpufd, KVM_SET_REGS, ®s); // 开启一个循环,执行VM的代码,并处理IO事件 while (1) { // 开启vCPU if(( ret = ioctl(vcpufd, KVM_RUN, NULL))==-1) { fprintf(stderr,"kvm Error:%san",strerror(errno)); exit(1); } // 当VM_Exit时,kvm_run结构中将保留exit_reason, 将基于此知悉退出原因,从而进行相应的处理 switch (run->exit_reason) { case KVM_EXIT_HLT: // VM关机 puts("KVM_EXIT_HLT"); return 0; case KVM_EXIT_IO: // IO事件 // 在这里,输出写入0x3f8的字符 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)); break; case KVM_EXIT_FAIL_ENTRY: fprintf(stderr, "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: fprintf(stderr, "KVM_EXIT_INTERNAL_ERROR: suberror = 0x%x", run->internal.suberror); default: fprintf(stderr, "exit_reason = 0x%x", run->exit_reason); } } }
vmware+centos7+linux 3.10 运行结果:
vmware+centos7+linux 5.10 运行结果:
在虚拟机设置中 点击 选项 在 共享文件夹 添加自己需要的共享文件夹
点击 安装VMware tool 或者更新
# root 用户操作 mkdir /mnt/cdrom # 挂载 mount -t iso9660 /dev/cdrom /mnt/cdrom ls /mnt/cdrom/ cp /mnt/cdrom/VMwareTools-xxx.tar.gz /root/vm.tar.gz ls tar -xzf vm.tar.gz cd vmware-tools-distrib # 安装, 一直回车,对于默认no的输入yes /vmware-install.pl # 最后一行 显示 --the VMware team # 查看之前共享的目录 cd /mnt/hgfs # 若没有hgfs目录 # 查看共享文件夹名是什么 vmware-hgfsclient # 没有hgfs的先创建 sudo mkdir hgfs # 若挂载过,先取消挂载 sudo umount /mnt/hgfs # 进行挂载 Linux是上面查找的共享文件夹 sudo /usr/bin/vmhgfs-fuse .host:/[填写上面的文件名] /mnt/hgfs -o allow_other -o uid=1000 -o gid=1000 -o umask=022 # 该方法重启可能会消失 sudo /usr/bin/vmhgfs-fuse .host:/linux-share /mnt/hgfs -o allow_other -o uid=1000 -o gid=1000 -o umask=022编译kvmtool
使用kvmtool启动一台最小虚拟机
# 克隆,或者自己下载kvmtool 解压 git clone https://github.com/kvmtool/kvmtool.git # unzip kvmtool-master.zip # sudo yum install unzip zip # mv kvmtool-master kvmtool cd kvmtool make # 编译出现 error: ‘value’ may be used uninitialized in this function # 方法一:修改Makefile 注释406行 CFLAGS += -Werror # 方法二:修改hw/i8042.c kbd_io 函数 298 行 为 u8 value = 0;参考
- 10行代码模拟QEMU/KVM的CPU虚拟化执行



