Qemu創建KVM虛擬機內存初始化流程


轉載請注明:【轉載自博客xelatex KVM】,並附本文鏈接。謝謝。

【注】文章中采用的版本:

  • Linux-3.11,https://www.kernel.org/pub/linux/kernel/v3.x/linux-3.11.tar.gz
  • qemu-kvm,git clone http://git.kernel.org/pub/scm/virt/kvm/qemu-kvm.git,
          git checkout 4d9367b76f71c6d938cf8201392abe4bfb1136cb

一、Qemu的內存模型

Qemu中的內存模型,簡單來說就是Qemu申請用戶態內存並進行管理,並將該部分申請的內存注冊到對應的加速器(如KVM)中。這樣的模型有如下好處:

  1. 策略與機制分離。加速的機制由KVM負責,而如何調用加速的機制由Qemu負責
  2. 可以由Qemu設置多種內存模型,如UMA、NUMA等等
  3. 方便Qemu對特殊內存的管理(如MMIO)
  4. 內存的分配、回收、換出等都可以采用Linux原有的機制,不需要為KVM單獨開發。
  5. 兼容其他加速器模型(或者無加速器,單純使用Qemu做模擬)

所以在初始化階段,Qemu需要做的有兩方面工作:向KVM注冊用戶態內存空間,申請用戶態內存空間。

Qemu主要通過如下結構來維護內存:

/* A system address space - I/O, memory, etc. */
struct AddressSpace {
    MemoryRegion *root;
    FlatView current_map;
    int ioeventfd_nb;
    MemoryRegionIoeventfd *ioeventfds;
};

AddressSpace設置了一段內存,其主要信息存儲在root成員中,root成員是個MemoryRegion結構,主要存儲內存區的結構。在Qemu中最主要的兩個AddressSpace是address_space_memory和address_space_io,分別對應的MemoryRegion變量是system_memory和system_io。

二、Qemu初始化KVM內存流程

Qemu的主函數是vl.c中的main函數,其中調用了configure_accelerator(),是KVM初始化的配置部分。

configure_accelerator中首先根據命令行輸入的參數找到對應的accelerator,這里是KVM。之后調用accel_list[i].init(),即kvm_init()。

在kvm_init()函數中主要做如下幾件事情:

  1. s->fd = qemu_open("/dev/kvm", O_RDWR),打開kvm控制的總設備文件/dev/kvm
  2. s->vmfd = kvm_ioctl(s, KVM_CREATE_VM, 0),調用創建虛擬機的API,對應Linux kernel中的創建流程,請全文搜索kernel,關鍵詞“KVM_CREATE_VM”
  3. kvm_check_extension,檢查各種extension,並設置對應的features
  4. ret = kvm_arch_init(s),做一些體系結構相關的初始化,如msr、identity map、mmu pages number等等
  5. kvm_irqchip_create,調用kvm_vm_ioctl(s, KVM_CREATE_IRQCHIP)在KVM中虛擬IRQ芯片,詳細流程請全文搜索
  6. memory_listener_register,該函數是初始化內存的主要函數,下面詳細分析

memory_listener_register調用了兩次,分別注冊了kvm_memory_listener和kvm_io_listener,即通用的內存和MMIO是分開管理的。以通用的內存注冊為例,函數首先在全局的memory_listener鏈表中添加了kvm_memory_listener,之后調用listener_add_address_space分別將該listener添加到address_space_memory和address_space_io中。

然后調用listener的region_add(即kvm_region_add()),該函數最終調用了kvm_set_user_memory_region(),其中調用kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem),該調用是最終將內存區域注冊到kvm中的函數。

之后在vl.c的main函數中調用了cpu_exec_init_all() => memory_map_init(),設置system_memory和system_io。

至此初始化好了所有Qemu中需要維護的相關的內存結構,並完成了在KVM中的注冊。下面需要初始化KVM中的MMU支持。

ram_size內存大小從內存被讀取到ram_size中,在vl.c的main中調用machine->init()來初始化,machine是命令行指定的機器類型,默認的init是pc_init_pci

  1. 調用pc_init1,參數分別是system_memory,system_io,ram_size,boot_device,kernel_filename,kernel_cmdline,initrd_filename,cpu_model,pci_enabled,kvmclock_enabled
    1. 設置above_4g_mem_size和below_4g_mem_size
    2. 調用pc_memory_init設置ram,第5、6個參數分別是below_4g_mem_size和above_4g_mem_size,MemoryRegion是system_memory
      1. 調用memory_region_init_ram初始化ram
        1. 調用memory_region_init()初始化MemoryRegion ram
        2. 設置destructor memory_region_destructor_ram
        3. 調用qemu_ram_alloc()初始化ram空間,內部調用qemu_ram_alloc_from_ptr()
          1. 如果沒有設置-mem-path參數,則進入到第二個else(if (xen_enabled()) {)
          2. if (kvm_enabled()),調用kvm_vmalloc
            1. 如果是TARGET_S390X,調用kvm_arch_vmalloc
            2. 否則調用qemu_vmalloc(此處走posix流程)
              1. 調用qemu_memalign->posix_memalign(),此處真正alloc memory並且aligned
            3. 或者直接調用malloc(在qemu_memalign中,沒有_POSIX_C_SOURCE和CONFIG_BSD
      2. 調用memory_region_init_alias初始化ram_below_4g,設置MemoryRegion的一個alias
      3. if (above_4g_mem_size > 0),調用memory_region_init_alias初始化ram_above_4g,設置alias
      4. memory_region_init_ram初始化pc.rom
      5. 調用bochs_bios_init初始化bios

這樣就建立好了Qemu-KVM的內存結構。在KVM創建Qemu內存映射的入口在kernel中kvm_vm_ioctl的case KVM_SET_USER_MEMORY_REGION,調用kvm_vm_ioctl_set_memory_region。kvm中用kvm_memory_slot結構來維護隊用戶空間地址的映射。該部分會在其他文章中詳細介紹。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM