作者 | 王柏生、謝廣軍
導讀:本文摘自于王柏生、謝廣軍撰寫的《深度探究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 interface
linux.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 interface
linux.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 8d20223edc81c6b199842b36fcd5b0aa1b8d3456
Dump KVM_EXIT_IO details
kvmtool.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