国产成人精品无码青草_亚洲国产美女精品久久久久∴_欧美人与鲁交大毛片免费_国产果冻豆传媒麻婆精东

15158846557 在線咨詢 在線咨詢
15158846557 在線咨詢
所在位置: 首頁(yè) > 營(yíng)銷資訊 > 網(wǎng)站運(yùn)營(yíng) > KVM虛擬機(jī)——QEMU代碼結(jié)構(gòu)分析

KVM虛擬機(jī)——QEMU代碼結(jié)構(gòu)分析

時(shí)間:2023-07-12 13:12:01 | 來(lái)源:網(wǎng)站運(yùn)營(yíng)

時(shí)間:2023-07-12 13:12:01 來(lái)源:網(wǎng)站運(yùn)營(yíng)

KVM虛擬機(jī)——QEMU代碼結(jié)構(gòu)分析:
轉(zhuǎn)載自:https://blog.csdn.net/yearn520/article/details/6602182
本文主要概括了QEMU的代碼結(jié)構(gòu),特別從代碼翻譯的角度分析了QEMU是如何將客戶機(jī)代碼翻譯成TCG代碼和主機(jī)代碼并且最終執(zhí)行的過(guò)程。并且在最后描述了QEMU和KVM之間聯(lián)系的紐帶。

申明:本文前面部分從qemu detailed study第七章翻譯而來(lái)。

視頻講解:

【DPDK/虛擬化技術(shù)】DPDK、KVM、qemu、虛擬化前后端virtio與vhost_嗶哩嗶哩_bilibili

1.代碼結(jié)構(gòu)

如我們所知,QEMU是一個(gè)模擬器,它能夠動(dòng)態(tài)模擬特定架構(gòu)的CPU指令,如X86,PPC,ARM等等。QEMU模擬的架構(gòu)叫目標(biāo)架構(gòu),運(yùn)行QEMU的系統(tǒng)架構(gòu)叫主機(jī)架構(gòu),QEMU中有一個(gè)模塊叫做微型代碼生成器(TCG),它用來(lái)將目標(biāo)代碼翻譯成主機(jī)代碼。如下圖所示。

我們也可以將運(yùn)行在虛擬cpu上的代碼叫做客戶機(jī)代碼,QEMU的主要功能就是不斷提取客戶機(jī)代碼并且轉(zhuǎn)化成主機(jī)指定架構(gòu)的代碼。整個(gè)翻譯任務(wù)分為兩個(gè)部分:第一個(gè)部分是將做目標(biāo)代碼(TB)轉(zhuǎn)化成TCG中間代碼,然后再將中間代碼轉(zhuǎn)化成主機(jī)代碼。

QEMU的代碼結(jié)構(gòu)非常清晰但是內(nèi)容非常復(fù)雜,這里先簡(jiǎn)單分析一下總體的結(jié)構(gòu)

1. 開(kāi)始執(zhí)行:

主要比較重要的c文件有:/vl.c,/cpus.c, /exec-all.c, /exec.c, /cpu-exec.c.

QEMU的main函數(shù)定義在/vl.c中,它也是執(zhí)行的起點(diǎn),這個(gè)函數(shù)的功能主要是建立一個(gè)虛擬的硬件環(huán)境。它通過(guò)參數(shù)的解析,將初始化內(nèi)存,需要的模擬的設(shè)備初始化,CPU參數(shù),初始化KVM等等。接著程序就跳轉(zhuǎn)到其他的執(zhí)行分支文件如:/cpus.c, /exec-all.c, /exec.c, /cpu-exec.c.

2. 硬件模擬

所有的硬件設(shè)備都在/hw/ 目錄下面,所有的設(shè)備都有獨(dú)自的文件,包括總線,串口,網(wǎng)卡,鼠標(biāo)等等。它們通過(guò)設(shè)備模塊串在一起,在vl.c中的machine _init中初始化。這里就不講每種設(shè)備是怎么實(shí)現(xiàn)的了。

3.目標(biāo)機(jī)器

現(xiàn)在QEMU模擬的CPU架構(gòu)有:Alpha, ARM, Cris, i386, M68K, PPC, Sparc, Mips, MicroBlaze, S390X and SH4.

我們?cè)赒EMU中使用./configure 可以配置運(yùn)行的架構(gòu),這個(gè)腳本會(huì)自動(dòng)讀取本機(jī)真實(shí)機(jī)器的CPU架構(gòu),并且編譯的時(shí)候就編譯對(duì)應(yīng)架構(gòu)的代碼。對(duì)于不同的QEMU做的事情都不同,所以不同架構(gòu)下的代碼在不同的目錄下面。/target-arch/目錄就對(duì)應(yīng)了相應(yīng)架構(gòu)的代碼,如/target-i386/就對(duì)應(yīng)了x86系列的代碼部分。雖然不同架構(gòu)做法不同,但是都是為了實(shí)現(xiàn)將對(duì)應(yīng)客戶機(jī)CPU架構(gòu)的TBs轉(zhuǎn)化成TCG的中間代碼。這個(gè)就是TCG的前半部分。

4.主機(jī)

這個(gè)部分就是使用TCG代碼生成主機(jī)的代碼,這部分代碼在/tcg/里面,在這個(gè)目錄里面也對(duì)應(yīng)了不同的架構(gòu),分別在不同的子目錄里面,如i386就在/tcg/i386中。整個(gè)生成主機(jī)代碼的過(guò)程也可以教TCG的后半部分。

5.文件總結(jié)和補(bǔ)充:

/vl.c: 最主要的模擬循環(huán),虛擬機(jī)機(jī)器環(huán)境初始化,和CPU的執(zhí)行。

/target-arch/translate.c 將客戶機(jī)代碼轉(zhuǎn)化成不同架構(gòu)的TCG操作碼。

/tcg/tcg.c 主要的TCG代碼。

/tcg/arch/tcg-target.c 將TCG代碼轉(zhuǎn)化生成主機(jī)代碼

/cpu-exec.c 其中的cpu-exec()函數(shù)主要尋找下一個(gè)TB(翻譯代碼塊),如果沒(méi)找到就請(qǐng)求得到下一個(gè)TB,并且操作生成的代碼塊。

2. TCG - 動(dòng)態(tài)翻譯

QEMU在 0.9.1版本之前使用DynGen翻譯c代碼.當(dāng)我們需要的時(shí)候TCG會(huì)動(dòng)態(tài)的轉(zhuǎn)變代碼,這個(gè)想法的目的是用更多的時(shí)間去執(zhí)行我們生成的代碼。當(dāng)新的代碼從TB中生成以后, 將會(huì)被保存到一個(gè)cache中,因?yàn)楹芏嘞嗤腡B會(huì)被反復(fù)的進(jìn)行操作,所以這樣類似于內(nèi)存的cache,能夠提高使用效率。而cache的刷新使用LRU算法。

編譯器在執(zhí)行器會(huì)從源代碼中產(chǎn)生目標(biāo)代碼,像GCC這種編譯器,它為了產(chǎn)生像函數(shù)調(diào)用目標(biāo)代碼會(huì)產(chǎn)生一些特殊的匯編目標(biāo)代碼,他們能夠讓編譯器需要知道在調(diào)用函數(shù)。需要什么,以及函數(shù)調(diào)用以后需要返回什么,這些特殊的匯編代碼產(chǎn)生過(guò)程就叫做函數(shù)的Prologue和Epilogue,這里就叫前端和后段吧。我在其他文章中也分析過(guò)匯編調(diào)用函數(shù)的過(guò)程,至于匯編里面函數(shù)調(diào)用過(guò)程中寄存器是如何變化的,在本文中就不再描述了。

函數(shù)的后端會(huì)恢復(fù)前端的狀態(tài),主要做下面2點(diǎn):

1. 恢復(fù)堆棧的指針,包括棧頂和基地址。
2. 修改cs和ip,程序回到之前的前端記錄點(diǎn)。
TCG就如編譯器一樣可以產(chǎn)生目標(biāo)代碼,代碼會(huì)保存在緩沖區(qū)中,當(dāng)進(jìn)入前端和后端的時(shí)候就會(huì)將TCG生成的緩沖代碼插入到目標(biāo)代碼中。

接下來(lái)我們就來(lái)看下如何翻譯代碼的:

客戶機(jī)代碼
TCG中間代碼
主機(jī)代碼

3. TB鏈

在QEMU中,從代碼cache到靜態(tài)代碼再回到代碼cache,這個(gè)過(guò)程比較耗時(shí),所以在QEMU中涉及了一個(gè)TB鏈將所有TB連在一起,可以讓一個(gè)TB執(zhí)行完以后直接跳到下一個(gè)TB,而不用每次都返回到靜態(tài)代碼部分。具體過(guò)程如下圖:

4. QEMU的TCG代碼分析

接下來(lái)來(lái)看看QEMU代碼中中到底怎么來(lái)執(zhí)行這個(gè)TCG的,看看它是如何生成主機(jī)代碼的。

main_loop(...){/vl.c} :

函數(shù)main_loop 初始化qemu_main_loop_start() 然后進(jìn)入無(wú)限循環(huán)cpu_exec_all() , 這個(gè)是QEMU的一個(gè)主要循環(huán),在里面會(huì)不斷的判斷一些條件,如虛擬機(jī)的關(guān)機(jī)斷電之類的。




qemu_main_loop_start(...){/cpus.c} :

函數(shù)設(shè)置系統(tǒng)變量 qemu_system_ready = 1并且重啟所有的線程并且等待一個(gè)條件變量。




cpu_exec_all(...){/cpus.c} :

它是cpu循環(huán),QEMU能夠啟動(dòng)256個(gè)cpu核,但是這些核將會(huì)分時(shí)運(yùn)行,然后執(zhí)行qemu_cpu_exec() 。




struct CPUState{/target-xyz/cpu.h} :

它是CPU狀態(tài)結(jié)構(gòu)體,關(guān)于cpu的各種狀態(tài),不同架構(gòu)下面還有不同。




cpu_exec(...){/cpu-exec.c}:

這個(gè)函數(shù)是主要的執(zhí)行循環(huán),這里第一次翻譯之前說(shuō)道德TB,TB被初始化為(TranslationBlock *tb) ,然后不停的執(zhí)行異常處理。其中嵌套了兩個(gè)無(wú)限循環(huán) find tb_find_fast() 和tcg_qemu_tb_exec().
cantb_find_fast()為客戶機(jī)初始化查詢下一個(gè)TB,并且生成主機(jī)代碼。
tcg_qemu_tb_exec()執(zhí)行生成的主機(jī)代碼




struct TranslationBlock {/exec-all.h}:

結(jié)構(gòu)體TranslationBlock包含下面的成員:PC, CS_BASE, Flags (表明TB), tc_ptr (指向這個(gè)TB翻譯代碼的指針), tb_next_offset[2], tb_jmp_offset[2] (接下去的Tb), *jmp_next[2], *jmp_first (之前的TB).




tb_find_fast(...){/cpu-exec.c} :

函數(shù)通過(guò)調(diào)用獲得程序指針計(jì)數(shù)器,然后傳到一個(gè)哈希函數(shù)從 tb_jmp_cache[] (一個(gè)哈希表)得到TB的所以,所以使用tb_jmp_cache可以找到下一個(gè)TB。如果沒(méi)有找到下一個(gè)TB,則使用tb_find_slow。




tb_find_slow(...){/cpu-exec.c}:

這個(gè)是在快速查找失敗以后試圖去訪問(wèn)物理內(nèi)存,尋找TB。




tb_gen_code(...){/exec.c}:

開(kāi)始分配一個(gè)新的TB,TB的PC是剛剛從CPUstate里面通過(guò)using get_page_addr_code()找到的phys_pc = get_page_addr_code(env, pc);
tb = tb_alloc(pc);
ph當(dāng)調(diào)用cpu_gen_code() 以后,接著會(huì)調(diào)用tb_link_page() ,它將增加一個(gè)新的TB,并且指向它的物理頁(yè)表。




cpu_gen_code(...){translate-all.c}:

函數(shù)初始化真正的代碼生成,在這個(gè)函數(shù)里面有下面的函數(shù)調(diào)用:
gen_intermediate_code(){/target-arch/translate.c}->gen_intermediate_code_internal(){/target-arch/translate.c }->disas_insn(){/target-arch/translate.c}




disas_insn(){/target-arch/translate.c}

函數(shù)disas_insn() 真正的實(shí)現(xiàn)將客戶機(jī)代碼翻譯成TCG代碼,它通過(guò)一長(zhǎng)串的switch case,將不同的指令做不同的翻譯,最后調(diào)用tcg_gen_code。




tcg_gen_code(...){/tcg/tcg.c}:

這個(gè)函數(shù)將TCG的代碼轉(zhuǎn)化成主機(jī)代碼,這個(gè)就不細(xì)細(xì)說(shuō)明了,和前面類似。




#define tcg_qemu_tb_exec(...){/tcg/tcg.g}:

通過(guò)上面的步驟,當(dāng)TB生成以后就通過(guò)這個(gè)函數(shù)進(jìn)行執(zhí)行.

next_tb = tcg_qemu_tb_exec(tc_ptr) :

extern uint8_t code_gen_prologue[];

#define tcg_qemu_tb_exec(tb_ptr) ((long REGPARM(*)(void *)) code_gen_prologue)(tb_ptr)




通過(guò)上面的步驟我們就解析了QEMU是如何將客戶機(jī)代碼翻譯成主機(jī)代碼的,了解了TCG的工作原理。接下來(lái)看看QEMU與KVM是怎么聯(lián)系的。




5. QEMU中的IOCTL

在QEMU-KVM中,用戶空間的QEMU是通過(guò)IOCTL與內(nèi)核空間的KVM模塊進(jìn)行通訊的。

1. 創(chuàng)建KVM

在/vl.c中通過(guò)kvm_init()將會(huì)創(chuàng)建各種KVM的結(jié)構(gòu)體變量,并且通過(guò)IOCTL與已經(jīng)初始化好的KVM模塊進(jìn)行通訊,創(chuàng)建虛擬機(jī)。然后創(chuàng)建VCPU,等等。

2. KVM_RUN

這個(gè)IOCTL是使用最頻繁的,整個(gè)KVM運(yùn)行就不停在執(zhí)行這個(gè)IOCTL,當(dāng)KVM需要QEMU處理一些指令和IO等等的時(shí)候就會(huì)退出通過(guò)這個(gè)IOCTL退回到QEMU進(jìn)行處理,不然就會(huì)一直在KVM中執(zhí)行。

它的初始化過(guò)程:

vl.c中調(diào)用machine->init初始化硬件設(shè)備接著調(diào)用pc_init_pci,然后再調(diào)用pc_init1。

接著通過(guò)下面的調(diào)用初始化KVM的主循環(huán),以及CPU循環(huán)。在CPU循環(huán)的過(guò)程中不斷的執(zhí)行KVM_RUN與KVM進(jìn)行交互。

pc_init1->pc_cpus_init->pc_new_cpu->cpu_x86_init->qemu_init_vcpu->kvm_init_vcpu->ap_main_loop->kvm_main_loop_cpu->kvm_cpu_exec->kvm_run




3.KVM_IRQ_LINE

這個(gè)IOCTL和KVM_RUN是不同步的,它也是個(gè)頻率非常高的調(diào)用,它就是一般中斷設(shè)備的中斷注入入口。當(dāng)設(shè)備有中斷就通過(guò)這個(gè)IOCTL最終調(diào)用KVM里面的kvm_set_irq將中斷注入到虛擬的中斷控制器。在kvm中會(huì)進(jìn)一步判斷屬于什么中斷類型,然后在合適的時(shí)機(jī)寫入vmcs。當(dāng)然在KVM_RUN中會(huì)不斷的同步虛擬中斷控制器,來(lái)獲取需要注入的中斷,這些中斷包括QEMU和KVM本身的,并在重新進(jìn)入客戶機(jī)之前注入中斷。




Dpdk/網(wǎng)絡(luò)協(xié)議棧/vpp/OvS/DDos/NFV/虛擬化/高性能專家-學(xué)習(xí)視頻教程
DPDK/網(wǎng)絡(luò)虛擬化 學(xué)習(xí)資料、教學(xué)視頻和學(xué)習(xí)路線圖有需要的可以自行添加學(xué)習(xí)交流群973961276 獲取


關(guān)鍵詞:結(jié)構(gòu),分析,虛擬

74
73
25
news

版權(quán)所有? 億企邦 1997-2025 保留一切法律許可權(quán)利。

為了最佳展示效果,本站不支持IE9及以下版本的瀏覽器,建議您使用谷歌Chrome瀏覽器。 點(diǎn)擊下載Chrome瀏覽器
關(guān)閉