天天看點

qemu-kvm 啟動

QEMU的核心初始化流程

客戶系統運作之前,QEMU作為全系統模拟軟體,需要為客戶系統模拟出CPU、主存以及I/O裝置,使客戶系統就像運作在真實硬體之上,而不用對客戶系統做修改。

如概覽部分所示,由使用者為客戶系統指定需要的虛拟CPU資源(包括CPU核心數,SOCKET數目,每核心的超線程數,是否開啟NUMA等等),虛拟記憶體資源,具體參數設定參見${QEMU}/qemu-options.hx。建立QEMU主線程,執行QEMU系統的初始化,在初始化的過程中針對每一個虛拟CPU,單獨建立一個posix線程。每當一個虛拟CPU線程被排程到實體CPU上執行時,該VCPU對應的一套完整的寄存器集合被加載到實體CPU上,通過VM-LAUNCH或VM-RESUME指令切換到非根模式執行。直到該線程時間片到,或者其它中斷引發虛拟機退出,VCPU退出到根模式,進行異常處理。

如下圖所示,當使用者運作QEMU的System Mode的可執行檔案時,QEMU從${QEMU}/vl.c的main函數執行主線程。以下着重分析,客戶系統啟動之前,QEMU所做的初始化工作:

qemu-kvm 啟動

1. 處理指令行參數:

進入vl.c的main函數,首先有一個很長的(;;)循環,用于分析處理通過指令行傳進來的參數,進行相應系統的初始化設定。比如建立多少VCPU,是否開啟NUMA,配置設定多少虛拟記憶體資源等等。

2. 選擇虛拟化方案:

configure_accelerator()函數,選擇使用哪種虛拟化解決方案。

accel_list[] = {

{ "tcg", "tcg", tcg_available, tcg_init, &tcg_allowed },

{ "xen", "Xen", xen_available, xen_init, &xen_allowed },

{ "kvm", "KVM", kvm_available, kvm_init, &kvm_allowed },

{ "qtest", "QTest", qtest_available, qtest_init, &qtest_allowed },

};

accel_list[]數組聲明了QEMU使用的系統模拟方案。“tcg”模式是不使用任何硬體虛拟化輔助方式,采用基于二進制指令翻譯的方式,将目标平台的指令代碼通過一個叫做TCG的子產品翻譯為本機可以執行的指令。“xen”、“kvm”分别為兩種主流的開源虛拟化解決方案。本文主要針對kvm這種硬體輔助的虛拟化解決方案。

qemu-kvm 啟動

3. 初始化記憶體布局:

新版本的QEMU(1.4)中,cpu_exec_init_all()函數隻負責注冊主存與IO記憶體兩個頂層的memory_region,并且注冊memory_listener。

qemu-kvm 啟動

4. 虛拟客戶機硬體初始化:

在完成了QEMU自身的初始化工作後,便開始了客戶系統核心的初始化工作,主要是QEMU根據指令行參數,為客戶系統建立虛拟的CPU、記憶體、I/O資源。核心過程是machine->init(&args),對于x86目标平台,實際調用的是pc_init1函數。下面着重分析該函數。

qemu-kvm 啟動

4.1 VCPU初始化pc_cpus_init()

void pc_cpus_init(const char *cpu_model)

{

int i;

if (cpu_model == NULL) {

#ifdef TARGET_X86_64

cpu_model = "qemu64";

#else

cpu_model = "qemu32";

#endif

}

for (i = 0; i < smp_cpus; i++) {

if (!cpu_x86_init(cpu_model)) {

fprintf(stderr, "Unable to find x86 CPU definition\n");

exit(1);

}

Pc_init1的函數調用關系如下圖所示,對于每一個即将建立的VCPU(個數由指令行傳入smp_cpus),執行cpu_x86_init,逐層調用後,由qemu_kvm_start_vcpu建立一個VCPU線程,新的VCPU線程将執行qemu_kvm_cpu_thread_fn函數,逐層調用後經過kvm_vcpu_ioctl系統調用切換到核心态,由KVM執行VCPU的建立工作,包括建立VMCS等非根模式下工作所需要的核心資料結構。

qemu-kvm 啟動

4.2 pc_memory_init初始化主存空間

4.3 i440fx_init初始化橋片及總線結構

4.4 pc_cmos_init初始化CMOS及時鐘

客戶系統的執行流程

qemu-kvm 啟動

在建立了全部VCPU後,這些VCPU線程并沒有被立即排程執行,直至vl.c的main函數執行完全部初始化工作後,調用resume_all_vcpus(),将pcpu->stop和 pcpu->stopped 置為false,當VCPU線程再次被排程到實體CPU上執行時,VCPU正式開始工作。

由于pc_memory_init階段已将BIOS初始化,建立了“bios.”檔案到記憶體的映射,已處于非根模式下的VCPU到客戶實體位址0xFFFF0(即“bios.”檔案被映射到的位址的一個線性偏移)處,擷取第一條指令,開始執行客戶系統代碼。

此時多個VCPU線程由宿主系統輪流排程執行,QEMU主線程處于循環中,用于接收來自客戶系統傳回的I/O模拟請求。每一個VCPU線程,隻要被排程的實體CPU上,便切到非根模式執行客戶系統代碼,産生需要退出的異常時(如EPT缺頁,處理I/O指令等),儲存異常原因到VMCS,切換到根模式下,由KVM捕獲該異常,查詢VMCS異常号執行相應處理,完成後再切換回非根模式,如此循環往複執行下去。

繼續閱讀