自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

內(nèi)存虛擬化到底是咋整的?

云計算 虛擬化
內(nèi)存虛擬化解決虛擬機里面的進程如何訪問物理機上的內(nèi)存這一問題。GuestOS本身有虛擬地址空間,用GVA表示。虛擬機認為自己獨占整個內(nèi)存空間,用GPA表示。

1. 一句話總結(jié) 

內(nèi)存虛擬化解決虛擬機里面的進程如何訪問物理機上的內(nèi)存這一問題。

GuestOS本身有虛擬地址空間,用GVA表示。虛擬機認為自己獨占整個內(nèi)存空間,用GPA表示。

HostOS本身有虛擬機地址空間,用HVA表示。宿主機本身有物理內(nèi)存空間,用HPA表示。

好,內(nèi)存虛擬化的問題變成了GVA->HPA的映射問題。

[[228006]]

GVA->GPA通過GuestOS頁表映射。HVA->HPA通過HostOS頁表映射。因此,只要建立GPA->HVA的映射關(guān)系,即可解決內(nèi)存虛擬化的問題。但,這樣三段逐次映射,效率低下。

引入軟件模擬的影子頁表和硬件輔助的EPT頁表。

影子頁表:GuestOS創(chuàng)建GVA->GPA頁表的時候,kvm知道GVA對應的HPA,并偷偷記錄下映射關(guān)系GVA->HPA。后續(xù)需要GVA到GPA映射的時候,根據(jù)影子頁表就能查到HPA。

EPT頁表:硬件層面引入EPTP寄存器。直接將Guest的CR3加載到宿主機的MMU中。同時EPT頁表被載入專門的EPT頁表指針寄存器 EPTP。也就是說GVA->GPA->HPA兩次地址轉(zhuǎn)換都由硬件實現(xiàn)。

2. 概述 

我們知道80386引入了保護模式后,內(nèi)存空間分為虛擬地址空間和物理地址空間。后續(xù)引入頁表機制,把虛擬機地址送往mmu,mmu查TLB不中的情況下,依次查頁表就可以找到對應的物理地址。

在虛擬化場景下情況略微復雜,分為以下幾種:

①GuestOS 虛擬地址(guestOS virtual Adress,GVA)

說白了guestos中進程使用的虛擬地址就是GVA,也就是程序訪問邏輯存儲器的地址。

②guestOS 物理地址(GuestOS Physical Address,GPA)

Guestos認為的物理地址,也是虛擬機mmu查頁表得出的地址但是他本質(zhì)是一個邏輯上的地址,是引入虛化后產(chǎn)生的一個邏輯概念。它必須借助于內(nèi)存虛擬化映射到宿主機的物理地址上才能訪問內(nèi)存

③主機虛擬機地址(Host virtul Address,HVA)

宿主機中的虛擬地址,宿主機進程使用的虛擬地址空間。

④主機物理地址(Host Physical Address,HPA)

宿主機真實內(nèi)存地址,真實可以訪問的物理內(nèi)存空間。

至此,在虛擬機場景下,如何由GVA->HPA就是內(nèi)存虛擬化的工作。其中,Qemu負責管理虛擬機內(nèi)存大小,記錄內(nèi)存對應的HVA地址(因為Qemu是用戶態(tài)的進程,無法管理HPA)想要轉(zhuǎn)化為HPA需要借助于KVM內(nèi)核也就是影子頁表SPT(Shadow Page Table)和EPT(Extent Page Table)

2.1 影子頁表

在Guestos建立頁表的時候,KVM偷偷的建立了一套指向宿主機物理地址的頁表??蛻魴C中的每一個頁表項都有一個影子頁表項與之相對應,就像其影子一樣。

在客戶機訪問內(nèi)存時,真正被裝入宿主機 MMU 的是客戶機當前頁表所對應的影子頁表這樣通過影子頁表就可以實現(xiàn)真正的內(nèi)存訪問虛擬機頁表和影子頁表通過一個哈希表建立關(guān)聯(lián)這樣通過頁目錄/頁表的客戶機物理地址就可以在哈希鏈表中快速地找到對應的影子頁目錄/頁表當客戶機切換進程時,客戶機操作系統(tǒng)會把待切換進程的頁表基址載入 CR3而 KVM 將會截獲這一特權(quán)指令,進行新的處理,也即在哈希表中找到與此頁表基址對應的影子頁表基址,載入客戶機 CR3使客戶機在恢復運行時 CR3 實際指向的是新切換進程對應的影子頁表。

2.2 EPT

EPT 技術(shù)在原有客戶機頁表對客戶機虛擬地址到客戶機物理地址映射的基礎(chǔ)上引入了 EPT頁表來實現(xiàn)客戶機物理地址到宿主機物理地址的另一次映射,這兩次地址映射都是由硬件自動完成??蛻魴C運行時,客戶機頁表被載入 CR3,而 EPT 頁表被載入專門的EPT 頁表指針寄存器 EPTP。

在客戶機物理地址到宿主機物理地址轉(zhuǎn)換的過程中,由于缺頁、寫權(quán)限不足等原因也會導致客戶機退出,產(chǎn)生 EPT異常。對于 EPT 缺頁異常,KVM首先根據(jù)引起異常的客戶機物理地址,映射到對應的宿主機虛擬地址,然后為此虛擬地址分配新的物理頁最后 KVM 再更新 EPT 頁表,建立起引起異常的客戶機物理地址到宿主機物理地址之間的映射。對 EPT 寫權(quán)限引起的異常,KVM 則通過更新相應的 EPT 頁表來解決。

由此可以看出,EPT 頁表相對于前述的影子頁表,其實現(xiàn)方式大大簡化。而且,由于客戶機內(nèi)部的缺頁異常也不會致使客戶機退出,因此提高了客戶機運行的性能。此外,KVM 只需為每個客戶機維護一套 EPT 頁表,也大大減少了內(nèi)存的額外開銷。

3. Qemu到KVM內(nèi)存管理 

3.1 設置鉤子 

main(vl.c)==>configure_accelerator==>kvm_init(kvm_all.c)==>memory_listener_register(&kvm_memory_listener,NULL);將kvm_memory_listener添加到memory_listeners鏈表中,將address_spaces和listener建立關(guān)聯(lián)

3.2 內(nèi)存對象初始化

main(vl.c)==>cpu_exec_init_all(exec.c)==>memory_map_init(exec.c)Qemu中系統(tǒng)內(nèi)存system_memory來管理,io內(nèi)存用system_io來管理。static MemoryRegion *system_memory.MemoryRegion可以有子區(qū)域。而memory_lister負責處理添加和移除內(nèi)存區(qū)域的管理。

3.3 內(nèi)存實例化 

pc_init1(hw\pc_piix.c)==>pc_memory_init這里主要分配整個內(nèi)存區(qū)域重點關(guān)注memory_region_init_ram方法memory_region_init_ram==>qemu_ram_alloc(獲得內(nèi)存的HVA記錄到)==>qemu_ram_alloc_internal==>ram_block_add(生成一個RAMBlock添加到ram_list,hva放到host字段)==>phys_mem_alloc==>qemu_anon_ram_alloc==>mmap

3.4 VM-Exit處理 

由于mmio導致的退出,相關(guān)處理如下kvm_cpu_exec==> case KVM_EXIT_MMIO==> cpu_physical_memory_rw==> address_space_rw==> io_mem_write

3.5 qemu到kvm的內(nèi)存調(diào)用接口

前面我們講到注冊過listener,當設置內(nèi)存時會調(diào)用到

static MemoryListener kvm_memory_listener = {

.region_add = kvm_region_add,

region_add==>kvm_region_add==>kvm_set_phys_mem

①物理起始地址和長度,在kvm_state中搜索已建立的KVMSlot *mem區(qū)域

②如果沒找到建立一個slot

==>kvm_set_user_memory_region(通知內(nèi)核態(tài)建立內(nèi)存區(qū)域)==>kvm_vm_ioctl(s, KVM_SET_USER_MEMORY_REGION, &mem)

3.6 KVM內(nèi)存處理 

kvm_vm_ioctl==>kvm_vm_ioctl_set_memory_region==>kvm_set_memory_region==>__kvm_set_memory_region內(nèi)核態(tài)也維護了一個slots,內(nèi)核態(tài)slot的管理策略是根據(jù)用戶空間的slot_id一一對應的slot =id_to_memslot(kvm->memslots, mem->slot);

①通過用戶態(tài)的slot獲取到內(nèi)核態(tài)對應結(jié)構(gòu)

②根據(jù)slot中的值和要設置的值,決定要操作的類別

③根據(jù)2中的動作進行操作

a.KVM_MR_CREATE: kvm_arch_create_memslot(做了一個3級的頁表)

b.KVM_MR_DELETE OR KVM_MR_MOVE:

申請一個slots,把kvm->memslots暫存到這里。首先通過id_to_memslot獲取準備插入的內(nèi)存條對應到kvm的插槽是slot。無論刪除還是移動,將其先標記為KVM_MEMSLOT_INVALID。然后是install_new_memslots,其實就是更新了一下slots->generation的值。

4. EPT相關(guān) 

4.1 EPT初始化 

kvm_arch_init==> kvm_mmu_module_init

①建立pte_list_desc_cache緩存結(jié)構(gòu)

②建立mmu_page_header_cache緩存結(jié)構(gòu),該結(jié)構(gòu)用于kvm_mmu_page

③register_shrinker(&mmu_shrinker);當系統(tǒng)內(nèi)存回收被調(diào)用時的鉤子

vcpu_create==>vmx_create_vcpu==>init_rmode_identity_map==>alloc_identity_pagetable==>__x86_set_memory_region

4.2 EPT載入

vcpu_enter_guest(struct kvm_vcpu *vcpu)==> kvm_mmu_reload(Guest的MMU初始化,為內(nèi)存虛擬化做準備)==> kvm_mmu_load==>mmu_topup_memory_caches==>mmu_alloc_roots-->mmu_alloc_direct_roots(根據(jù)當前vcpu的分頁模式建立 ept頂層頁表的管理結(jié)構(gòu))==>kvm_mmu_sync_roots

4.3 gfn_to_page 

該函數(shù)處理GPA的頁號到HPA的page結(jié)構(gòu):

  1. gfn_to_page==>gfn_to_pfn==>gfn_to_pfn_memslot==>__gfn_to_pfn_memslot==>__gfn_to_hva_many|hva_to_pfn==>hva_to_pfn_fast|hva_to_pfn_slow 

4.4 分配頁表

  1. mmu_alloc_roots-->mmu_alloc_direct_roots-->kvm_mmu_get_page-->kvm_mmu_alloc_page 

4.5 EPT vm-entry 

①KVM_REQ_MMU_RELOAD-->kvm_mmu_unload-->mmu_free_roots

②KVM_REQ_MMU_SYNC-->kvm_mmu_sync_roots-->mmu_sync_roots-->mmu_sync_children-->kvm_sync_page-->__kvm_sync_page

③KVM_REQ_TLB_FLUSH-->kvm_vcpu_flush_tlb-->tlb_flush-->vmx_flush_tlb-->__vmx_flush_tlb-->ept_sync_context-->__invept

進入非根模式下,根據(jù)不同事件針對內(nèi)存做相關(guān)處理。

4.6 EPT VM-exit 

①設置cr3

mmu_alloc_direct_roots中會分配arch.mmu.root_hpavcpu_enter_guest的時候會調(diào)用kvm_mmu_load==> vcpu->arch.mmu.set_cr3(vcpu,vcpu->arch.mmu.root_hpa)這個函數(shù)要申請內(nèi)存,作為根頁表使用。同時root_hpa指向根頁表的物理地址。然后可以看到,vcpu中cr3寄存器的地址要指向這個根頁表的物理地址。

②handle_ept_violation

  1. -->kvm_mmu_page_fault-->arch.mmu.page_fault-->tdp_page_fault 

__direct_map 這個函數(shù)是根據(jù)傳進來的gpa進行計算,從第4級(level-4)頁表頁開始,一級一級地填寫相應頁表項這些都是在for_each_shadow_entry(vcpu, (u64)gfn << PAGE_SHIFT, iterator) 這個宏定義里面實現(xiàn)的.這兩種情況是這樣子的:

a.如果當前頁表頁的層數(shù)(iterator.level )是最后一層( level )的頁表頁,那么直接通過調(diào)用 mmu_set_spte (之后會細講)設置頁表項。

b.如果當前頁表頁 A 不是最后一層,而是中間某一層(leve-4, level-3, level-2)

而且該頁表項之前并沒有初始化(!is_shadow_present_pte(*iterator.sptep) )那么需要調(diào)用kvm_mmu_get_page 得到或者新建一個頁表頁 B然后通過 link_shadow_page 將其link到頁表頁 A 相對應的頁表項中

4.7 EPT遍歷操作

for_each_shadow_entry這個是定義在mmu.c中的一個宏,用來不斷的遍歷頁表的層級。

4.8 影子頁表 

init_kvm_mmu==>init_kvm_softmmu

在上述的ept的過程中,根據(jù)參數(shù)不同會有不同分支大體邏輯保持一致,毋庸贅言。

 

責任編輯:武曉燕 來源: 騰訊云TStack
相關(guān)推薦

2018-12-09 16:52:01

無線充電無線

2023-10-10 16:03:48

數(shù)字化信息化

2016-11-17 22:18:31

id串行化服務器

2022-08-08 08:00:00

人工智能機器學習計算機應用

2024-02-22 08:00:00

SoraOpenAI

2022-06-07 23:33:53

數(shù)字化轉(zhuǎn)型企業(yè)轉(zhuǎn)型數(shù)字化

2022-05-24 17:00:41

區(qū)塊鏈IT比特幣

2013-11-18 10:34:00

企業(yè)移動化移動信息化

2024-03-15 08:06:58

MySQLJOIN命令

2015-04-21 09:20:40

SwfitObject—C

2016-08-04 14:53:34

服務器虛擬化網(wǎng)絡

2021-02-05 10:03:31

區(qū)塊鏈技術(shù)智能

2020-12-28 08:18:55

安全代碼線程

2024-05-11 09:41:45

線程安全代碼

2022-04-10 19:26:07

TypeScript類型語法

2015-10-09 11:01:07

iPhone原創(chuàng)鎖定

2020-08-19 07:48:11

云計算亞馬遜搜索

2010-04-02 16:46:43

云計算

2022-08-12 08:03:59

算力網(wǎng)絡算力網(wǎng)絡

2013-04-24 09:08:17

Google眼鏡
點贊
收藏

51CTO技術(shù)棧公眾號