6.kvm源码分析-10.KVM源码-《计算机知识》

admin 2025-11-02 22:28:05 系统网络 来源:ZONE.CI 全球网 0 阅读模式
  • 相关参考
  • KVM概述
  • 内核KVM源码目录及组成
  • KVM初始化流程
    • 模块初始化:
      • 初始化流程-我们先大概浏览下框架**
    • 初始化-kvm_arch_init
    • 初始化-kvm_arch_hardware_setup
  • QEMU-KVM创建虚拟机
  • qemu 源码分析
    • 驱动初始化函数
    • 入口函数总览
    • qemu_init初始化函数
      • module_call_init
      • qemu_add_opts和qemu_add_drive_opts
      • argv_parse

    相关参考

    • BOOK-STACK-QEMU KVM学习笔记
    • Intel手册
    • 2. KVM模块初始化
    • x86 CPU的MSR寄存器
    • CPU_ID

    KVM概述

    image.pngKVM特性

    • KVM基于Intel VT-x或者AMD-V等硬件技术的全虚拟化。
    • KVM是基于Linux内核基础,加了虚拟化的部分管理的模块。
    • KVM由KVM-kernel驱动部分+QEMU-KVM应用部分组成。

    内核KVM源码目录及组成

    KVM源码目录树分析内核源码组成

    先看下Intel X86的虚拟化部分Makefile

    1. arch/x86/kvm/Makefile
    2. ccflags-y += -Iarch/x86/kvm
    3. KVM := ../../../virt/kvm
    4. kvm-y += $(KVM)/kvm_main.o $(KVM)/coalesced_mmio.o
    5. kvm-y += x86.o emulate.o i8259.o irq.o lapic.o \
    6. i8254.o ioapic.o irq_comm.o cpuid.o pmu.o mtrr.o \
    7. hyperv.o debugfs.o mmu/mmu.o mmu/page_track.o
    8. kvm-intel-y += vmx/vmx.o vmx/vmenter.o vmx/pmu_intel.o vmx/vmcs12.o vmx/evmcs.o vmx/nested.o
    9. obj-$(CONFIG_KVM) += kvm.o
    10. obj-$(CONFIG_KVM_INTEL) += kvm-intel.o

    可以看出,Kvm主要由kvm和kvm-intel组成

    1. kvm.o是kvm的核心模块
    • IOMMU、中断控制、设备管理、kvm arch等部分代码
    • kvm并没有完全实现一整个PC系统虚拟化,而仅仅将部分重要的CPU虚拟化、I/O虚拟化和内存虚拟化部分针对硬件辅助的能力进行有效地抽象和对接,其他一些模块需要借助于Qemu
    • 也就是说,kvm基本只实现硬件辅助虚拟化相关部分,而不支持的用Qemu来模拟实现
    1. kvm-intel.o是intel平台架构虚拟化模块,平台相关

      KVM初始化流程

      使用环境:ubuntu 18.04 Desktop,参考 /boot/config-5.4.0-52-generic 配置

      模块初始化:

      image.pngimage.png

    整理流程代码概述

    1. // linux/x86/linux-5.7.14/arch/x86/kvm/vmx/vmx.c
    2. static struct kvm_x86_init_ops vmx_init_ops __initdata = {
    3. .cpu_has_kvm_support = cpu_has_kvm_support,
    4. .disabled_by_bios = vmx_disabled_by_bios,
    5. .check_processor_compatibility = vmx_check_processor_compat,
    6. .hardware_setup = hardware_setup,
    7. .runtime_ops = &vmx_x86_ops,
    8. };
    1. // linux/x86/linux-5.7.14/arch/x86/kvm/vmx/vmx.c
    2. vmx_init() // 初始化入口
    3. ├─ kvm_init(KVM_GET_API_VERSION) // 初始化KVM框架
    4. | ├─ kvm_arch_init() // 架构相关初始化
    5. | | ├─ cpu_has_kvm_support() // CPU是否支持kvm
    6. | | ├─ disabled_by_bios() // bios是否禁用vt
    7. | | ├─ boot_cpu_has() // CPU是否支持一些特性
    8. | | ├─ kmem_cache_create("x86_fpu") // x86_fpu kmem_cache
    9. | | ├─ alloc_percpu() // kvm_shared_msrs
    10. | | ├─ kvm_mmu_module_init() // mmu模块初始化
    11. | | ├─ kvm_mmu_set_mask_ptes() // shadow pte mask设置
    12. | | ├─ kvm_timer_init() // 时钟初始化
    13. | | ├─ kvm_lapic_init() // lapic初始化
    14. | ├─ kvm_irqfd_init() //
    15. | ├─ kvm_arch_hardware_setup() //
    16. | | ├─ kvm_x86_ops->hardware_setup() // CPU是否支持kvm
    17. | | | ├─ rdmsrl_safe() // 读msr
    18. | | | ├─ store_idt() // 保存idt
    19. | | | ├─ setup_vmcs_config() // 建立vmcs_config和vmx_capability
    20. | | | ├─ boot_cpu_has() // CPU特性支持
    21. | | | ├─ cpu_has_vmx_vpid() // cpu是否支持vpid
    22. | | | ├─ cpu_has_vmx_invvpid() // cpu是否支持invvpid
    23. | | | ├─ cpu_has_vmx_ept() // cpu是否支持ept
    24. | | | ├─ kvm_configure_mmu() // mmu相关硬件判断和全局变量
    25. | | | ├─ cpu_has_vmx_XXX() // cpu是否有XXX
    26. | | | ├─ vmx_enable_tdp() // ept支持时开启tdp
    27. | | | ├─ kvm_disable_tdp() // 关闭tdp
    28. | | | ├─ kvm_set_posted_intr_wakeup_handler() // posted intr wakeup handler
    29. | | | └─ alloc_kvm_area() // 给每个cpu分配一个struct vmcs
    30. | | └─ kvm_init_msr_list() // 将msr保存到全局变量msrs_to_save[]数组
    31. | ├─ smp_call_function_single() // 对每个online cpu进行兼容性检查
    32. | ├─ cpuhp_setup_state_nocalls() // 注册cpu状态变化的回调函数
    33. | ├─ register_reboot_notifier() // 注册reboot时候的通知函数
    34. | ├─ kvm_cache_create_usercopy() // 创建vcpu 的 kmem cache, 对象大小是sizeof(struct vcpu_vmx)
    35. | ├─ kvm_async_pf_init() // 异步
    36. | ├─ misc_register(&kvm_dev) // 注册字符设备文件/dev/kvm
    37. | ├─ register_syscore_ops() // 注册系统核心函数, 这里是suspend和resume
    38. | ├─ kvm_init_debug() // 初始化debugfs
    39. | └─ kvm_vfio_ops_init() // vfio的操作初始化
    40. ├─ vmx_setup_l1d_flush() //
    41. └─ vmx_check_vmcs12_offsets()

    初始化流程-我们先大概浏览下框架**
    1. vmx_init // vmx/vmx.c
    2. // IS_ENABLED(CONFIG_HYPERV) 这里不考虑,HYPER-V是windows的虚拟化产品
    3. kvm_init // virt/kvm_main.c
    4. kvm_arch_init // kvm/x86/x86.c 硬件架构相关初始化,判断硬件支持能力
    5. kvm_irqfd_init // 创建kvm-irqfd-cleanup 工作队列
    6. zalloc_cpumask_var
    7. kvm_arch_hardware_setup
    8. for_each_online_cpu(cpu)
    9. smp_call_function_single(cpu, check_processor_compat, &c, 1);
    10. cpuhp_setup_state_nocalls
    11. register_reboot_notifier
    12. kvm_async_pf_init
    13. misc_register(&kvm_dev); // qemu对用户层接口:/dev/kvm接口注册-使用misc混杂设备
    14. register_syscore_ops(&kvm_syscore_ops);
    15. kvm_init_debug(); // debugfs init创建
    16. kvm_vfio_ops_init();
    17. .....

    初始化-kvm_arch_init

    1. kvm_arch_init() // 这里操作接口传入 vmx_init_ops
    2. /******* 第一部分:KVM的硬件环境检测 ********/
    3. kvm_x86_ops.hardware_enable // 这里用来判断是否使能过-没使能,否则重复加载错误
    4. // cpu_has_vmx 汇编读取CPU-ID,判断CPU硬件是否支持虚拟化
    5. ops->cpu_has_kvm_support()
    6. // vmx_disabled_by_bios 读取BIOS是否禁止了 vmx
    7. ops->disabled_by_bios()
    8. // kvm明确规定guest是需要FPU浮点支持和FXSAVE/FXRSTOR指令支持
    9. !boot_cpu_has(X86_FEATURE_FPU) || !boot_cpu_has(X86_FEATURE_FXSR)
    10. /******** 第二部分:KVM资源分配 **********/
    11. // __alignof__(struct fpu) 判断结构体内部是多少字节对齐
    12. x86_fpu_cache = kmem_cache_create // 创建SLAB高速缓存 struct fpu
    13. x86_emulator_cache = kvm_alloc_emulator_cache // emulator-仿真,看名称是创建仿真高速缓存
    14. shared_msrs = alloc_percpu(struct kvm_shared_msrs); // 给每个CPU都有一个msrs变量
    15. /******** 第三部分:MMU相关 **********/
    16. kvm_mmu_module_init
    17. __set_nx_huge_pages(get_nx_auto_mode()); // 不研究
    18. kvm_mmu_reset_all_pte_masks(); //
    19. kvm_set_mmio_spte_mask();
    20. kvm_mmu_set_mask_ptes
    21. /******** 第四部分:混杂项 **********/
    22. kvm_timer_init

    初始化-kvm_arch_hardware_setup

    1. kvm_arch_hardware_setup
    2. dmsrl_safe(MSR_EFER, &host_efer);
    3. ops->hardware_setup(); // X86的 hardware_setup 函数
    4. memcpy(&kvm_x86_ops, ops->runtime_ops, sizeof(kvm_x86_ops)); // 这里有个run time ops

    kvm_get_shadow_phys_bits

    1. / *
    2. *当在CPU检测代码中检测到MKTME或SME时,boot_cpu_data.x86_phys_bits会减少,
    3. 但是处理器会将这些减少的位视为“ keyID”,因此它们不是保留位。
    4. 因此,KVM需要查看CPUID报告的物理地址位。
    5. * /
    6. if (likely(boot_cpu_data.extended_cpuid_level >= 0x80000008))
    7. return cpuid_eax(0x80000008) & 0xff;
    8. / *
    9. *非常奇怪有VMX或SVM但没有MAXPHYADDR; 可能是具有自定义CPUID的VM。 继续执行发现的所有内核,因为这些功能不可虚拟化(SME / SEV还需要大于0x80000008的CPUID)。
    10. * /
    11. return boot_cpu_data.x86_phys_bits;
    12. / *

    QEMU-KVM创建虚拟机

    1. kvm_dev_ioctl
    2. kvm_dev_ioctl_create_vm(arg); // case KVM_CREATE_VM:
    3. kvm_create_vm(type);
    4. kvm_coalesced_mmio_init(kvm);
    5. get_unused_fd_flags(O_CLOEXEC);
    6. anon_inode_getfile("kvm-vm", &kvm_vm_fops, kvm, O_RDWR);
    7. kvm_create_vm_debugfs(kvm, r)
    8. kvm_uevent_notify_change(KVM_EVENT_CREATE_VM, kvm);
    9. fd_install(r, file);

    qemu 源码分析

    在《qemu调试系统》中描述了如何创建一个X86的虚拟机,这里简要贴下如何编译qemu和使用qemu:

    1. cd qemu
    2. ./configure --prefix=/opt/x86_64/qemu-x86-bin --target-list=x86_64-softmmu --gdb=/usr/bin/gdb \
    3. --enable-linux-aio --enable-debug --enable-debug-info
    4. sudo make && sudo make install
    5. sudo echo "PATH=\$PATH:/usr/local/qemu_x86/bin" >> ~/.bashrc
    6. source ~/.bashrc
    7. which qemu-system-x86_64
    8. /usr/local/qemu_x86/bin/qemu-system-x86_64 \
    9. -smp 2 \
    10. -cpu host \
    11. -enable-kvm \
    12. -m 512M \
    13. -kernel linux/arch/x86/boot/bzImage \
    14. -hda ./x86_64.img \
    15. -hdb ./Freeze.img \
    16. -nographic \
    17. -append "root=/dev/sda rw rootfstype=ext4 console=ttyS0 init=linuxrc loglevel=8"

    驱动初始化函数

    QEMU初始化代码并不是在main入口函数中执行,而是有一部分驱动初始化模块:

    1. #define module_init(function, type) \
    2. static void __attribute__((constructor)) do_qemu_init_ ## function(void) \
    3. { \
    4. register_module_init(function, type); \
    5. }
    6. #define block_init(function) module_init(function, MODULE_INIT_BLOCK)
    7. #define opts_init(function) module_init(function, MODULE_INIT_OPTS)
    8. #define type_init(function) module_init(function, MODULE_INIT_QOM)
    9. #define trace_init(function) module_init(function, MODULE_INIT_TRACE)
    10. #define xen_backend_init(function) module_init(function, \
    11. MODULE_INIT_XEN_BACKEND)
    12. #define libqos_init(function) module_init(function, MODULE_INIT_LIBQOS)
    13. #define fuzz_target_init(function) module_init(function, \
    14. MODULE_INIT_FUZZ_TARGET)
    15. #define migration_init(function) module_init(function, MODULE_INIT_MIGRATION)

    可以看到,attribute((constructor))用法解析 中讲述了: constructor 修饰的属性,在main函数之前执行。会将所有相同的驱动模块加入到同一个类型的链表中。 10.KVM源码 - 图410.KVM源码 - 图5所以软件中 module_call_init(MODULE_INIT_XXX) 会遍历当前节点的所有设备进行初始化

    入口函数总览

    10.KVM源码 - 图6

    qemu_init初始化函数

    10.KVM源码 - 图7

    module_call_init

    在 驱动初始化函数 部分描述了,系统上电后,会将所有的设备注册到对应类型的链表中,这个接口是调用了当前接口类型下所有设备的init函数 进行初始化。

    qemu_add_opts和qemu_add_drive_opts

    1. static QemuOptsList *vm_config_groups[48];
    2. static QemuOptsList *drive_config_groups[5];

    添加 操作到对应的配置或驱动组 模块

    argv_parse

    这个函数最终会解析:qemu-options.def 中的结构体信息进行描述,这里在编译时会打包到 static const QEMUOption qemu_options[]中;

    1. const QEMUOption *popt; popt = lookup_opt(argc, argv, &optarg, &optind);
    2. popt = qemu_options; // 这个函数会从这个全局变量里边查找*****
    3. typedef struct QEMUOption {
    4. const char *name;
    5. int flags;
    6. int index;
    7. uint32_t arch_mask;
    8. } QEMUOption;
    9. static const QEMUOption qemu_options[] = {
    10. { "h", 0, QEMU_OPTION_h, QEMU_ARCH_ALL },
    11. #define QEMU_OPTIONS_GENERATE_OPTIONS
    12. #include "qemu-options-wrapper.h"
    13. { NULL },
    14. };
    15. #define DEF(option, opt_arg, opt_enum, opt_help, arch_mask) \
    16. { option, opt_arg, opt_enum, arch_mask },
    17. #define DEFHEADING(text)
    18. #define ARCHHEADING(text, arch_mask)
    19. #include "qemu-options.def"
    01-shell脚本介绍-《shell脚本》 系统网络

    01-shell脚本介绍-《shell脚本》

    一、shell脚本是什么二、为什么要学shell,而不是其他计算机语言三、学习这门课程的优势四、学了能干什么五、学习什么内容六、学习的技巧七、成长路径八、学习环
    评论:0   参与:  15