formal(讀懂 x86 架構 CPU 虛擬化,看這文就夠了 | 贈書)

時間:2023-08-18 22:05:27 閱讀:2

讀懂 x86 架構 CPU 假造化,看這文就夠了 | 贈書

作者 | 王柏生、謝廣軍

導讀:本文摘自于王柏生、謝廣軍撰寫的《深度探究Linux體系假造化:原理與完成》一書,先容了CPU假造化的基本看法,探究了x86架構在假造化時面臨的停滯,以及為支持CPU假造化,Intel在硬件層面完成的擴展VMX。

同時,先容了在VMX擴展支持下,假造CPU從Host形式到Guest形式,再回到Host形式的完備生命周期。

Gerald J. Popek和Robert P. Goldberg在1974年公布的論文“Formal Requirements for Virtualizable Third Generation Architectures”中提出了假造化的3個條件:

1)等價性,即VMM必要在宿主機上為假造機模仿出一個實質上與物理機一律的情況。假造機在這個情況上運轉與其在物理機上運轉別無二致,除了約莫由于資源競爭大概VMM的干涉招致在假造情況中體現略有差別,好比假造機的I/O、網絡等因宿主機的限速大概多個假造機共享資源,招致速率約莫要比獨占物理機時慢一些。

2)高效性,即假造機指令實行的功能與其在物理機上運轉比擬并無分明斲喪。該標準要求假造機中的絕大局部指令無須VMM干涉而直接運轉在物理CPU上,好比我們在x86架構上經過Qemu運轉的ARM體系并不是假造化,而是模仿。

3)資源控制,即VMM可以完全控制體系資源。由VMM控制和諧宿主機資源給各個假造機,而不克不及由假造機控制了宿主機的資源。

墮入和模仿模子

為了滿意Gerald J. Popek和Robert P. Goldberg提出的假造化的3個條件,一個典范的處理方案是墮入和模仿(Trap and Emulate)模子。

尋常來說,處理器分為兩種運轉形式:體系形式和用戶形式。相應地,CPU的指令也分為特權指令和非特權指令。

特權指令只能在體系形式運轉,假如在用戶形式運轉就將觸發處理器特別。利用體系允許內核運轉在體系形式,由于內核必要辦理體系資源,必要運轉特權指令,而平凡的用戶步驟則運轉在用戶形式。

在墮入和模仿模子下,假造機的用戶步驟仍舊運轉在用戶形式,但是假造機的內核也將運轉在用戶形式,這種辦法稱為特權級緊縮(Ring Compression)。在這種辦法下,假造機中的非特權指令直接運轉在處理器上,滿意了假造化標準中高效的要求,即大局部指令無須VMM干涉直接在處理器上運轉。

但是,當假造機實行特權指令時,由于是在用戶形式下運轉,將觸發處理器特別,從而墮入VMM中,由VMM署理假造機完成體系資源的拜候,即所謂的模仿(emulate)。

云云,又滿意了假造化標準中VMM控制體系資源的要求,假造機將不會由于可以直接運轉特權指令而修正宿主機的資源,從而毀壞宿主機的情況。

x86架構假造化的停滯

Gerald J. Popek和Robert P. Goldberg指出,修正體系資源的,大概在不同形式下舉動有不同體現的,都屬于敏感指令。

在假造化場景下,VMM必要監測這些敏感指令。一個支持假造化的體系架構的敏感指令都屬于特權指令,即在非特權級別實行這些敏感指令時CPU會拋出特別,進入VMM的特別處理函數,從而完成了控制VM拜候敏感資源的目標。

但是,x86架構恰好不克不及滿意這個準則。x86架構并不是一切的敏感指令都是特權指令,有些敏感指令在非特權形式下實行時并不會拋出特別,此時VMM就無法攔阻處理VM的舉動了。

我們以修正FLAGS存放器中的IF(Interrupt Flag)為例,我們起首使用指令pushf將FLAGS存放器的內容壓到棧中,然后將棧頂的IF清零,最初使用popf指令從棧中規復FLAGS存放器。假如假造機內核沒有運轉在ring 0,x86的CPU并不會拋出特別,而只是靜靜地忽略指令popf,因此假造布局閉IF的目標并沒有奏效。

有人提出半假造化的處理方案,即修正Guest的代碼,但是這不切合假造化的純透準則。厥后,人們提出了二進制翻譯的方案,包含靜態翻譯和動態翻譯。靜態翻譯就是在運轉前掃描整個可實行文件,對敏感指令舉行翻譯,構成一個新的文件。

但是,靜態翻譯必需事先處理,并且關于有些指令僅有在運轉時才會產生的反作用,無法靜態處理。于是,動態翻譯應運而生,即在運轉時以代碼塊為單位動態地修正二進制代碼。動態翻譯在很多VMM中取得使用,并且優化的后果十分不錯。

VMX

固然各位從軟件層面接納了多種方案來處理x86架構在假造化時碰到的成績,但是這些處理方案除了引入了分外的開支外,還給VMM的完成帶來了宏大的繁復性。于是,Intel實驗從硬件層面處理這個成績。

Intel并沒有將那些非特權的敏感指令修正為特權指令,由于并不是一切的特權指令都必要攔阻處理。舉一個典范的例子,每當利用體系內核切換歷程時,都市切換cr3存放器,使其指向如今運轉歷程的頁表。

但是,當使用影子頁表舉行GVA到HPA的映射時,VMM模塊必要捕捉Guest每一次設置cr3存放器的利用,使其指向影子頁表。而當啟用了硬件層面的EPT支持后,cr3存放器不再必要指向影子頁表,其仍舊指向Guest的歷程的頁表。

因此,VMM無須再捕捉Guest設置cr3存放器的利用,也就是說,固然寫cr3存放器是一個特權益用,但這個利用不必要墮入VMM。

Intel開發了VT武藝以支持假造化,為CPU增長了Virtual-Machine Extensions,簡稱VMX。一旦啟動了CPU的VMX支持,CPU將提供兩種運轉形式:VMX Root Mode和VMX non-Root Mode,每一種形式都支持ring 0 ~ ring 3。

VMM運轉在VMX Root Mode,除了支持VMX外,VMX Root Mode和平凡的形式并無實質區別。VM運轉在VMX non-Root Mode,Guest無須再接納特權級緊縮辦法,Guest kernel可以直接運轉在VMX non-Root Mode的ring 0中,如圖1所示。

圖1 VMX運轉形式

處于VMX Root Mode的VMM可以經過實行CPU提供的假造化指令VMLaunch切換到VMX non-Root Mode,由于這個歷程相當于進入Guest,以是通常也被稱為VM entry。

當Guest內里實行了敏感指令,好比某些I/O利用后,將觸發CPU產生墮入的舉措,從VMX non-Root Mode切換回VMX Root Mode,這個歷程相當于退去VM,以是也稱為VM exit。

然后VMM將對Guest 的利用舉行模仿。比擬于將Guest的內核也運轉在用戶形式(ring 1 ~ ring 3)的辦法,支持VMX的CPU有以下3點不同:

1)運轉于Guest形式時,Guest用戶空間的體系調用直接墮入Guest形式的內核空間,而不再是墮入Host形式的內核空間。

2)關于外部中綴,由于必要由VMM控制體系的資源,以是處于Guest形式的CPU收到外部中綴后,則觸發CPU從Guest形式退去到Host形式,由Host內核處理外部中綴。

處理完中綴后,再重新切入Guest形式。為了提高I/O聽從,Intel支持外設透傳形式,在這種形式下,Guest不必產生VM exit,“裝備假造化”一章將討論這種特別辦法。

3)不再是一切的特權指令都市招致處于Guest形式的CPU產生VM exit,僅當運轉敏感指令時才會招致CPU從Guest形式墮入Host形式,由于有的特權指令并不必要由VMM到場處理。

好像一個CPU可以分時運轉多個職責一樣,每個職責有本人的上下文,由調治器在調治時切換上下文,從而完成同一個CPU同時運轉多個職責。在假造化場景下,同一個物理CPU“一人分飾多角”,分時運轉著Host及Guest,在不同形式間按需切換,因此,不同形式也必要保存本人的上下文。

為此,VMX計劃了一個保存上下文的數據布局:VMCS。每一個Guest都有一個VMCS實例,當物理CPU加載了不同的VMCS時,將運轉不同的Guest如圖2所示。

圖2 多個Guest切換

VMCS中主要保存著兩大類數據,一類是形態,包含Host的形態和Guest的形態,別的一類是控制Guest運轉時的舉動。此中:

1)Guest-state area,保存假造機形態的地區。當產生VM exit時,Guest的形態將保存在這個地區;當VM entry時,這些形態將被裝載到CPU中。這些都是硬件層面的主動舉動,無須VMM編碼干涉。

2)Host-state area,保存宿主機形態的地區。當產生VM entry時,CPU主動將宿主機形態保存到這個地區;當產生VM exit時,CPU主動從VMCS規復宿主機形態到物理CPU。

3)VM-exit information fields。當假造機產生VM exit時,VMM必要曉得招致VM exit的緣故,然后才干“對癥下藥”,舉行相應的模仿利用。為此,CPU會主動將Guest退去的緣故保存在這個地區,供VMM使用。

4)VM-execution control fields。這個地區中的種種字段控制著假造機運轉時的一些舉動,好比設置Guest運轉時拜候cr3存放器時對否觸發VM exit;控制VM entry與VM exit時舉動的VM-entry control fields和VM-exit control fields。別的另有很多不同功效的地區,我們不再逐一擺列,讀者如有必要可以查閱Intel手冊。

在創建VCPU時,KVM模塊將為每個VCPU哀求一個VMCS,每次CPU準備切入Guest形式時,將設置其VMCS指針指向即將切入的Guest對應的VMCS實例:

commit 6aa8b732ca01c3d7a54e93f4d701b8aabbe60fb7[PATCH] kvm: userspace interfacelinux.git/drivers/kvm/vmx.c
static struct kvm_vcpu *vmx_vcpu_load(struct kvm_vcpu *vcpu){ u64 phys_addr = __pa(vcpu->vmcs); int cpu;
cpu = get_cpu; if (per_cpu(current_vmcs, cpu) != vcpu->vmcs) { per_cpu(current_vmcs, cpu) = vcpu->vmcs; asm volatile (ASM_VMX_VMPTRLD_RAX "; setna %0" : "=g"(error) : "a"(&phys_addr), "m"(phys_addr) : "cc"); }}

并不是一切的形態都由CPU主動保存與規復,我們還必要思索聽從。以cr2存放器為例,大大多時分,從Guest退去Host到再次進入Guest時期,Host并不會改動cr2存放器的值,并且寫cr2的開支很大,假如每次VM entry時都更新一次cr2,除了糜費CPU的算力毫偶然義。因此,將這些形態交給VMM,由軟件自行控制更為公道。

VCPU生命周期

關于每個假造處理器(VCPU),VMM使用一個線程來代表VCPU這個實體。在Guest運轉歷程中,每個VCPU基本都在如圖3所示的形態中不休地轉換。

圖3 VCPU生命周期

1)在用戶空間準備好后,VCPU地點線程向內核中KVM模塊倡導一個ioctl哀求KVM_RUN,見告內核中的KVM模塊,用戶空間的利用以前完成,可以切入Guest形式運轉Guest了。

2)在進入內核態后,KVM模塊將調用CPU提供的假造化指令切入Guest形式。假如是初次運轉Guest,則使用VMLaunch指令,不然使用VMResume指令。在這個切換歷程中,起首,CPU的形態(也就是Host的形態)將會被保存到VMCS中存儲Host形態的地區,非CPU主動保存的形態由KVM賣力保存。然后,加載存儲在VMCS中的Guest的形態到物理CPU,非CPU主動規復的形態則由KVM賣力規復。

3)物理CPU切入Guest形式,運轉Guest指令。當實行Guest指令碰到敏感指令時,CPU將從Guest形式切回到Host形式的ring 0,進入Host內核的KVM模塊。在這個切換歷程中,起首,CPU的形態(也就是Guest的形態)將會被保存到VMCS中存儲Guest形態的地區,然后,加載存儲在VMCS中的Host的形態到物理CPU。相反的,非CPU主動保存的形態由KVM模塊賣力保存。

4)處于內核態的KVM模塊從VMCS中讀取假造機退去緣故,實驗在內核中處理。假如內核中可以處理,那么假造機就不必再切換到Host形式的用戶態了,處理完后,直接快速切回Guest。這種退去也稱為輕量級假造機退去。

5)假如內核態的KVM模塊不克不及處理假造機退去,那么VCPU將再舉行一次上下文切換,從Host的內核態切換到Host的用戶態,由VMM的用戶空間局部舉行處理。VMM用戶空間處理終了,再次倡導切入Guest形式的指令。在整個假造機運轉歷程中,步調1~5循環往復。

底下是KVM切入、切出Guest的代碼:

commit 6aa8b732ca01c3d7a54e93f4d701b8aabbe60fb7[PATCH] kvm: userspace interfacelinux.git/drivers/kvm/vmx.c
static int vmx_vcpu_run(struct kvm_vcpu *vcpu, …){ u8 fail; u16 fs_sel, gs_sel, ldt_sel; int fs_gs_ldt_reload_needed;
again: /* Enter guest mode */ "jne launched \n\t" ASM_VMX_VMLAUNCH "\n\t" "jmp kvm_vmx_return \n\t" "launched: " ASM_VMX_VMRESUME "\n\t" ".globl kvm_vmx_return \n\t" "kvm_vmx_return: " /* Save guest registers, load host registers, keep flags */ if (kvm_handle_exit(kvm_run, vcpu)) { goto again; } } return 0;}

在從Guest退去時,KVM模塊起首調用函數kvm_handle_exit實驗在內核空間處理Guest退去。函數kvm_handle_exit有個商定,假如在內核空間可以告捷處理假造機退去,大概是由于別的干擾好比外部中綴招致假造機退去等無須切換到Host的用戶空間,則前往1;

不然前往0,表現必要告急KVM的用戶空間處理假造機退去,好比必要KVM用戶空間的模仿裝備處理外設哀求。

假如內核空間告捷處理了假造機的退去,則函數kvm_handle_exit前往1,在上述代碼中即直接跳轉到標簽again處,然后步驟流程會再次切入Guest。

假如函數kvm_handle_exit前往0,則函數vmx_vcpu_run完畢實行,CPU從內核空間前往到用戶空間,以kvmtool為例,其干系代碼片斷如下:

commit 8d20223edc81c6b199842b36fcd5b0aa1b8d3456Dump KVM_EXIT_IO detailskvmtool.git/kvm.c
int main(int argc, char *argv[]){ for (;;) { kvm__run(kvm);
switch (kvm->kvm_run->exit_reason) { case KVM_EXIT_IO: }}

依據代碼可見,kvmtool倡導進入Guest的代碼處于一個for的無窮循環中。當從KVM內核空間前往用戶空間后,kvmtool在用戶空間處理Guest的哀求,好比調用模仿裝備處理I/O哀求。

在處理完Guest的哀求后,重新進入下一輪for循環,kvmtool再次哀求KVM模塊切入Guest。

福利

CSDN 攜手【機器產業出書社】送出

《深度探究Linux體系假造化:原理與完成》一本

停止11月25日18:00點

作者簡介:

王柏生

資深武藝專家,先后就職于中科院軟件所、紅旗Linux和百度,現任百度主任架構師。在利用體系、假造化武藝、分布式體系、云盤算、主動駕駛等干系范疇耕作多年,有著豐厚的實踐履歷。

著有熱銷書《深度探究Linux利用體系》(2013年出書)。

謝廣軍

盤算機專業博士,畢業于南開大學盤算機系。

資深武藝專家,有多年的IT行業事情履歷?,F承繼百度智能云副總司理,賣力云盤算干系產物的研發。多年來不休從事利用體系、假造化武藝、分布式體系、大數據、云盤算等干系范疇的研發事情,實踐履歷豐厚。

版權聲明:本文來自互聯網整理發布,如有侵權,聯系刪除

原文鏈接:http://www.freetextsend.comhttp://www.freetextsend.com/qingganjiaoliu/34326.html


Copyright ? 2021-2022 All Rights Reserved 備案編號:閩ICP備2023009674號 網站地圖 聯系:dhh0407@outlook.com

www.成人网