尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Linux kvmtool Kernel Virtual Machine Tool and ramfs Loading

Linux kvmtool Kernel Virtual Machine Tool and ramfs Loading Linux kvmtool Kernel Virtual Machine Tool and ramfs Loadingkvmtool也称为lkvm或kvm-tool是比QEMU轻量得多的KVM用户态VMM源码位于tools/kvm/目录。它的核心设计哲学是直接使用KVM API避免QEMU的完整设备模型专注于快速启动Guest Linux内核。ramfsinitramfs/rootfs加载是kvmtool启动流程的关键环节决定了Guest内核完成初始化的根文件系统来源。kvmtool的主入口在kvm.c的kvm__init和kvm__startcint kvm__init(struct kvm *kvm){int ret;/* 打开/dev/kvm获取KVM文件描述符 */kvm-sys_fd open(/dev/kvm, O_RDWR);if (kvm-sys_fd 0)return -errno;/* 通过KVM_API_VERSION检查接口版本兼容性 */ret ioctl(kvm-sys_fd, KVM_GET_API_VERSION, 0);if (ret ! KVM_API_VERSION)die(KVM API version mismatch);/* 创建VM fd */kvm-vm_fd ioctl(kvm-sys_fd, KVM_CREATE_VM, 0);/* 获取KVM支持的能力 */ioctl(kvm-sys_fd, KVM_CHECK_EXTENSION, KVM_CAP_IRQCHIP);ioctl(kvm-sys_fd, KVM_CHECK_EXTENSION, KVM_CAP_USER_MEMORY);return 0;}int kvm__start(struct kvm *kvm){/* 加载内核elf镜像 */if (kvm-cfg.firmware_filename)load_bzimage(kvm, kvm-cfg.firmware_filename);/* 加载initramfs */if (kvm-cfg.initrd_filename)load_ramdisk(kvm, kvm-cfg.initrd_filename);/* 设置启动参数 */kvm__setup_bootargs(kvm);/* 创建VCPU并启动 */kvm__create_vcpus(kvm);kvm__run(kvm);return 0;}ramfs加载通过load_ramdisk函数实现将initramfs或rootfs镜像加载到Guest物理内存中的指定位置cint load_ramdisk(struct kvm *kvm, const char *filename){int fd;ssize_t file_size;struct stat st;unsigned long addr;const char *p;/* 打开ramdisk文件 */fd open(filename, O_RDONLY);if (fd 0)return -errno;/* 获取文件大小 */if (fstat(fd, st) 0) {close(fd);return -errno;}file_size st.st_size;/* 计算ramdisk在Guest物理内存中的地址* 通常放在内核加载地址之后紧接内核末尾*/addr kvm-arch.kern_start kvm-arch.kern_size;/* 4KB对齐 */addr (addr 0xfff) ~0xfff;/* 使用KVM_SET_USER_MEMORY_REGION映射 */if (!kvm-ram_size_enough(addr, file_size)) {/* 扩展Guest物理内存映射 */kvm__register_mem(kvm, addr, ALIGN(file_size, PAGE_SIZE),NULL);}/* 使用mmap映射文件到Guest地址空间 */p mmap(NULL, file_size, PROT_READ,MAP_PRIVATE | MAP_POPULATE, fd, 0);if (p MAP_FAILED) {close(fd);return -errno;}/* 拷贝ramdisk内容到Guest内存 */memcpy(guest_flat_to_host(kvm, addr), p, file_size);munmap((void *)p, file_size);close(fd);/* 更新内核启动参数中的ramdisk地址和大小 */kvm-arch.ramdisk_addr addr;kvm-arch.ramdisk_size file_size;pr_debug( RAM disk: %s, 0x%lx, size %ld bytes\n,filename, addr, file_size);return 0;}加载完成后kvmtool通过setup_bootargs在boot_params结构中填充initrd_start和initrd_size字段。x86架构下boot_params由Linux boot protocol定义位于实模式启动代码段0x90000偏移处。kvmtool使用kvm__setup_boot_args直接在Guest物理内存的boot_params结构体上写入cvoid kvm__setup_boot_args(struct kvm *kvm){struct boot_params *boot;unsigned long bp_addr 0x90000;/* 获取boot_params的Host指针 */boot guest_flat_to_host(kvm, bp_addr);/* 填充initrd地址和大小 */boot-hdr.ramdisk_image kvm-arch.ramdisk_addr;boot-hdr.ramdisk_size kvm-arch.ramdisk_size;/* 设置启动参数行 */if (kvm-cfg.real_cmdline)strncpy((char *)boot-hdr.cmd_line_ptr,kvm-cfg.real_cmdline, 255);/* 设置header标志 */boot-hdr.type_of_loader 0xFF;boot-hdr.loadflags | CAN_USE_HEAP | LOAD_HIGH;}kvmtool的ramfs加载路径与QEMU有显著区别。QEMU通过fw_cfg将initramfs传递给SeaBIOS或OVMF而kvmtool直接在内核启动参数中设置initrd地址由内核本身在启动过程中解压并挂载initramfs。这种方式减少了启动链中的中间环节。kvmtool的Guest内存映射使用kvm__register_mem创建KVM_SET_USER_MEMORY_REGION slot每个slot对应一段连续的Guest物理地址范围。ramfs加载完成后内核解压initramfs时通过arch/x86/kernel/setup.c中的populate_initrd_image访问这些地址。如果ramfs大于可用slot大小kvmtool会合并或扩展已有slot来容纳。内存映射的核心数据结构为cstruct kvm_mem_bank {struct list_head list;u64 guest_phys_addr;u64 size;void *host_addr;struct kvm_userspace_memory_region region;};每个mem_bank对应一个KVM memory slotkvmtool通过KVM_SET_USER_MEMORY_REGION将host_addr线性映射到guest_phys_addr供Guest内核直接访问。
返回列表