作者 | 王柏生、謝廣軍
導(dǎo)讀:本文摘自于王柏生、謝廣軍撰寫的《深度探究Linux體系假造化:原理與完成》一書,先容了CPU假造化的基本看法,探究了x86架構(gòu)在假造化時(shí)面臨的停滯,以及為支持CPU假造化,Intel在硬件層面完成的擴(kuò)展VMX。
同時(shí),先容了在VMX擴(kuò)展支持下,假造CPU從Host形式到Guest形式,再回到Host形式的完備生命周期。
Gerald J. Popek和Robert P. Goldberg在1974年公布的論文“Formal Requirements for Virtualizable Third Generation Architectures”中提出了假造化的3個(gè)條件:
1)等價(jià)性,即VMM必要在宿主機(jī)上為假造機(jī)模仿出一個(gè)實(shí)質(zhì)上與物理機(jī)一律的情況。假造機(jī)在這個(gè)情況上運(yùn)轉(zhuǎn)與其在物理機(jī)上運(yùn)轉(zhuǎn)別無二致,除了約莫由于資源競(jìng)爭大概VMM的干涉招致在假造情況中體現(xiàn)略有差別,好比假造機(jī)的I/O、網(wǎng)絡(luò)等因宿主機(jī)的限速大概多個(gè)假造機(jī)共享資源,招致速率約莫要比獨(dú)占物理機(jī)時(shí)慢一些。
2)高效性,即假造機(jī)指令實(shí)行的功能與其在物理機(jī)上運(yùn)轉(zhuǎn)比擬并無分明斲喪。該標(biāo)準(zhǔn)要求假造機(jī)中的絕大局部指令無須VMM干涉而直接運(yùn)轉(zhuǎn)在物理CPU上,好比我們?cè)趚86架構(gòu)上經(jīng)過Qemu運(yùn)轉(zhuǎn)的ARM體系并不是假造化,而是模仿。
3)資源控制,即VMM可以完全控制體系資源。由VMM控制和諧宿主機(jī)資源給各個(gè)假造機(jī),而不克不及由假造機(jī)控制了宿主機(jī)的資源。
墮入和模仿模子
為了滿意Gerald J. Popek和Robert P. Goldberg提出的假造化的3個(gè)條件,一個(gè)典范的處理方案是墮入和模仿(Trap and Emulate)模子。
尋常來說,處理器分為兩種運(yùn)轉(zhuǎn)形式:體系形式和用戶形式。相應(yīng)地,CPU的指令也分為特權(quán)指令和非特權(quán)指令。
特權(quán)指令只能在體系形式運(yùn)轉(zhuǎn),假如在用戶形式運(yùn)轉(zhuǎn)就將觸發(fā)處理器特別。利用體系允許內(nèi)核運(yùn)轉(zhuǎn)在體系形式,由于內(nèi)核必要辦理體系資源,必要運(yùn)轉(zhuǎn)特權(quán)指令,而平凡的用戶步驟則運(yùn)轉(zhuǎn)在用戶形式。
在墮入和模仿模子下,假造機(jī)的用戶步驟仍舊運(yùn)轉(zhuǎn)在用戶形式,但是假造機(jī)的內(nèi)核也將運(yùn)轉(zhuǎn)在用戶形式,這種辦法稱為特權(quán)級(jí)緊縮(Ring Compression)。在這種辦法下,假造機(jī)中的非特權(quán)指令直接運(yùn)轉(zhuǎn)在處理器上,滿意了假造化標(biāo)準(zhǔn)中高效的要求,即大局部指令無須VMM干涉直接在處理器上運(yùn)轉(zhuǎn)。
但是,當(dāng)假造機(jī)實(shí)行特權(quán)指令時(shí),由于是在用戶形式下運(yùn)轉(zhuǎn),將觸發(fā)處理器特別,從而墮入VMM中,由VMM署理假造機(jī)完成體系資源的拜候,即所謂的模仿(emulate)。
云云,又滿意了假造化標(biāo)準(zhǔn)中VMM控制體系資源的要求,假造機(jī)將不會(huì)由于可以直接運(yùn)轉(zhuǎn)特權(quán)指令而修正宿主機(jī)的資源,從而毀壞宿主機(jī)的情況。
x86架構(gòu)假造化的停滯
Gerald J. Popek和Robert P. Goldberg指出,修正體系資源的,大概在不同形式下舉動(dòng)有不同體現(xiàn)的,都屬于敏感指令。
在假造化場(chǎng)景下,VMM必要監(jiān)測(cè)這些敏感指令。一個(gè)支持假造化的體系架構(gòu)的敏感指令都屬于特權(quán)指令,即在非特權(quán)級(jí)別實(shí)行這些敏感指令時(shí)CPU會(huì)拋出特別,進(jìn)入VMM的特別處理函數(shù),從而完成了控制VM拜候敏感資源的目標(biāo)。
但是,x86架構(gòu)恰好不克不及滿意這個(gè)準(zhǔn)則。x86架構(gòu)并不是一切的敏感指令都是特權(quán)指令,有些敏感指令在非特權(quán)形式下實(shí)行時(shí)并不會(huì)拋出特別,此時(shí)VMM就無法攔阻處理VM的舉動(dòng)了。
我們以修正FLAGS存放器中的IF(Interrupt Flag)為例,我們起首使用指令pushf將FLAGS存放器的內(nèi)容壓到棧中,然后將棧頂?shù)腎F清零,最初使用popf指令從棧中規(guī)復(fù)FLAGS存放器。假如假造機(jī)內(nèi)核沒有運(yùn)轉(zhuǎn)在ring 0,x86的CPU并不會(huì)拋出特別,而只是靜靜地忽略指令popf,因此假造布局閉IF的目標(biāo)并沒有奏效。
有人提出半假造化的處理方案,即修正Guest的代碼,但是這不切合假造化的純透準(zhǔn)則。厥后,人們提出了二進(jìn)制翻譯的方案,包含靜態(tài)翻譯和動(dòng)態(tài)翻譯。靜態(tài)翻譯就是在運(yùn)轉(zhuǎn)前掃描整個(gè)可實(shí)行文件,對(duì)敏感指令舉行翻譯,構(gòu)成一個(gè)新的文件。
但是,靜態(tài)翻譯必需事先處理,并且關(guān)于有些指令僅有在運(yùn)轉(zhuǎn)時(shí)才會(huì)產(chǎn)生的反作用,無法靜態(tài)處理。于是,動(dòng)態(tài)翻譯應(yīng)運(yùn)而生,即在運(yùn)轉(zhuǎn)時(shí)以代碼塊為單位動(dòng)態(tài)地修正二進(jìn)制代碼。動(dòng)態(tài)翻譯在很多VMM中取得使用,并且優(yōu)化的后果十分不錯(cuò)。
VMX
固然各位從軟件層面接納了多種方案來處理x86架構(gòu)在假造化時(shí)碰到的成績,但是這些處理方案除了引入了分外的開支外,還給VMM的完成帶來了宏大的繁復(fù)性。于是,Intel實(shí)驗(yàn)從硬件層面處理這個(gè)成績。
Intel并沒有將那些非特權(quán)的敏感指令修正為特權(quán)指令,由于并不是一切的特權(quán)指令都必要攔阻處理。舉一個(gè)典范的例子,每當(dāng)利用體系內(nèi)核切換歷程時(shí),都市切換cr3存放器,使其指向如今運(yùn)轉(zhuǎn)歷程的頁表。
但是,當(dāng)使用影子頁表舉行GVA到HPA的映射時(shí),VMM模塊必要捕捉Guest每一次設(shè)置cr3存放器的利用,使其指向影子頁表。而當(dāng)啟用了硬件層面的EPT支持后,cr3存放器不再必要指向影子頁表,其仍舊指向Guest的歷程的頁表。
因此,VMM無須再捕捉Guest設(shè)置cr3存放器的利用,也就是說,固然寫cr3存放器是一個(gè)特權(quán)益用,但這個(gè)利用不必要墮入VMM。
Intel開發(fā)了VT武藝以支持假造化,為CPU增長了Virtual-Machine Extensions,簡稱VMX。一旦啟動(dòng)了CPU的VMX支持,CPU將提供兩種運(yùn)轉(zhuǎn)形式:VMX Root Mode和VMX non-Root Mode,每一種形式都支持ring 0 ~ ring 3。
VMM運(yùn)轉(zhuǎn)在VMX Root Mode,除了支持VMX外,VMX Root Mode和平凡的形式并無實(shí)質(zhì)區(qū)別。VM運(yùn)轉(zhuǎn)在VMX non-Root Mode,Guest無須再接納特權(quán)級(jí)緊縮辦法,Guest kernel可以直接運(yùn)轉(zhuǎn)在VMX non-Root Mode的ring 0中,如圖1所示。
圖1 VMX運(yùn)轉(zhuǎn)形式
處于VMX Root Mode的VMM可以經(jīng)過實(shí)行CPU提供的假造化指令VMLaunch切換到VMX non-Root Mode,由于這個(gè)歷程相當(dāng)于進(jìn)入Guest,以是通常也被稱為VM entry。
當(dāng)Guest內(nèi)里實(shí)行了敏感指令,好比某些I/O利用后,將觸發(fā)CPU產(chǎn)生墮入的舉措,從VMX non-Root Mode切換回VMX Root Mode,這個(gè)歷程相當(dāng)于退去VM,以是也稱為VM exit。
然后VMM將對(duì)Guest 的利用舉行模仿。比擬于將Guest的內(nèi)核也運(yùn)轉(zhuǎn)在用戶形式(ring 1 ~ ring 3)的辦法,支持VMX的CPU有以下3點(diǎn)不同:
1)運(yùn)轉(zhuǎn)于Guest形式時(shí),Guest用戶空間的體系調(diào)用直接墮入Guest形式的內(nèi)核空間,而不再是墮入Host形式的內(nèi)核空間。
2)關(guān)于外部中綴,由于必要由VMM控制體系的資源,以是處于Guest形式的CPU收到外部中綴后,則觸發(fā)CPU從Guest形式退去到Host形式,由Host內(nèi)核處理外部中綴。
處理完中綴后,再重新切入Guest形式。為了提高I/O聽從,Intel支持外設(shè)透?jìng)餍问剑谶@種形式下,Guest不必產(chǎn)生VM exit,“裝備假造化”一章將討論這種特別辦法。
3)不再是一切的特權(quán)指令都市招致處于Guest形式的CPU產(chǎn)生VM exit,僅當(dāng)運(yùn)轉(zhuǎn)敏感指令時(shí)才會(huì)招致CPU從Guest形式墮入Host形式,由于有的特權(quán)指令并不必要由VMM到場(chǎng)處理。
好像一個(gè)CPU可以分時(shí)運(yùn)轉(zhuǎn)多個(gè)職責(zé)一樣,每個(gè)職責(zé)有本人的上下文,由調(diào)治器在調(diào)治時(shí)切換上下文,從而完成同一個(gè)CPU同時(shí)運(yùn)轉(zhuǎn)多個(gè)職責(zé)。在假造化場(chǎng)景下,同一個(gè)物理CPU“一人分飾多角”,分時(shí)運(yùn)轉(zhuǎn)著Host及Guest,在不同形式間按需切換,因此,不同形式也必要保存本人的上下文。
為此,VMX計(jì)劃了一個(gè)保存上下文的數(shù)據(jù)布局:VMCS。每一個(gè)Guest都有一個(gè)VMCS實(shí)例,當(dāng)物理CPU加載了不同的VMCS時(shí),將運(yùn)轉(zhuǎn)不同的Guest如圖2所示。
圖2 多個(gè)Guest切換
VMCS中主要保存著兩大類數(shù)據(jù),一類是形態(tài),包含Host的形態(tài)和Guest的形態(tài),別的一類是控制Guest運(yùn)轉(zhuǎn)時(shí)的舉動(dòng)。此中:
1)Guest-state area,保存假造機(jī)形態(tài)的地區(qū)。當(dāng)產(chǎn)生VM exit時(shí),Guest的形態(tài)將保存在這個(gè)地區(qū);當(dāng)VM entry時(shí),這些形態(tài)將被裝載到CPU中。這些都是硬件層面的主動(dòng)舉動(dòng),無須VMM編碼干涉。
2)Host-state area,保存宿主機(jī)形態(tài)的地區(qū)。當(dāng)產(chǎn)生VM entry時(shí),CPU主動(dòng)將宿主機(jī)形態(tài)保存到這個(gè)地區(qū);當(dāng)產(chǎn)生VM exit時(shí),CPU主動(dòng)從VMCS規(guī)復(fù)宿主機(jī)形態(tài)到物理CPU。
3)VM-exit information fields。當(dāng)假造機(jī)產(chǎn)生VM exit時(shí),VMM必要曉得招致VM exit的緣故,然后才干“對(duì)癥下藥”,舉行相應(yīng)的模仿利用。為此,CPU會(huì)主動(dòng)將Guest退去的緣故保存在這個(gè)地區(qū),供VMM使用。
4)VM-execution control fields。這個(gè)地區(qū)中的種種字段控制著假造機(jī)運(yùn)轉(zhuǎn)時(shí)的一些舉動(dòng),好比設(shè)置Guest運(yùn)轉(zhuǎn)時(shí)拜候cr3存放器時(shí)對(duì)否觸發(fā)VM exit;控制VM entry與VM exit時(shí)舉動(dòng)的VM-entry control fields和VM-exit control fields。別的另有很多不同功效的地區(qū),我們不再逐一擺列,讀者如有必要可以查閱Intel手冊(cè)。
在創(chuàng)建VCPU時(shí),KVM模塊將為每個(gè)VCPU哀求一個(gè)VMCS,每次CPU準(zhǔn)備切入Guest形式時(shí),將設(shè)置其VMCS指針指向即將切入的Guest對(duì)應(yīng)的VMCS實(shí)例:
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");
…
}
…
}
并不是一切的形態(tài)都由CPU主動(dòng)保存與規(guī)復(fù),我們還必要思索聽從。以cr2存放器為例,大大多時(shí)分,從Guest退去Host到再次進(jìn)入Guest時(shí)期,Host并不會(huì)改動(dòng)cr2存放器的值,并且寫cr2的開支很大,假如每次VM entry時(shí)都更新一次cr2,除了糜費(fèi)CPU的算力毫偶然義。因此,將這些形態(tài)交給VMM,由軟件自行控制更為公道。
VCPU生命周期
關(guān)于每個(gè)假造處理器(VCPU),VMM使用一個(gè)線程來代表VCPU這個(gè)實(shí)體。在Guest運(yùn)轉(zhuǎn)歷程中,每個(gè)VCPU基本都在如圖3所示的形態(tài)中不休地轉(zhuǎn)換。
圖3 VCPU生命周期
1)在用戶空間準(zhǔn)備好后,VCPU地點(diǎn)線程向內(nèi)核中KVM模塊倡導(dǎo)一個(gè)ioctl哀求KVM_RUN,見告內(nèi)核中的KVM模塊,用戶空間的利用以前完成,可以切入Guest形式運(yùn)轉(zhuǎn)Guest了。
2)在進(jìn)入內(nèi)核態(tài)后,KVM模塊將調(diào)用CPU提供的假造化指令切入Guest形式。假如是初次運(yùn)轉(zhuǎn)Guest,則使用VMLaunch指令,不然使用VMResume指令。在這個(gè)切換歷程中,起首,CPU的形態(tài)(也就是Host的形態(tài))將會(huì)被保存到VMCS中存儲(chǔ)Host形態(tài)的地區(qū),非CPU主動(dòng)保存的形態(tài)由KVM賣力保存。然后,加載存儲(chǔ)在VMCS中的Guest的形態(tài)到物理CPU,非CPU主動(dòng)規(guī)復(fù)的形態(tài)則由KVM賣力規(guī)復(fù)。
3)物理CPU切入Guest形式,運(yùn)轉(zhuǎn)Guest指令。當(dāng)實(shí)行Guest指令碰到敏感指令時(shí),CPU將從Guest形式切回到Host形式的ring 0,進(jìn)入Host內(nèi)核的KVM模塊。在這個(gè)切換歷程中,起首,CPU的形態(tài)(也就是Guest的形態(tài))將會(huì)被保存到VMCS中存儲(chǔ)Guest形態(tài)的地區(qū),然后,加載存儲(chǔ)在VMCS中的Host的形態(tài)到物理CPU。相反的,非CPU主動(dòng)保存的形態(tài)由KVM模塊賣力保存。
4)處于內(nèi)核態(tài)的KVM模塊從VMCS中讀取假造機(jī)退去緣故,實(shí)驗(yàn)在內(nèi)核中處理。假如內(nèi)核中可以處理,那么假造機(jī)就不必再切換到Host形式的用戶態(tài)了,處理完后,直接快速切回Guest。這種退去也稱為輕量級(jí)假造機(jī)退去。
5)假如內(nèi)核態(tài)的KVM模塊不克不及處理假造機(jī)退去,那么VCPU將再舉行一次上下文切換,從Host的內(nèi)核態(tài)切換到Host的用戶態(tài),由VMM的用戶空間局部舉行處理。VMM用戶空間處理終了,再次倡導(dǎo)切入Guest形式的指令。在整個(gè)假造機(jī)運(yùn)轉(zhuǎn)歷程中,步調(diào)1~5循環(huán)往復(fù)。
底下是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退去時(shí),KVM模塊起首調(diào)用函數(shù)kvm_handle_exit實(shí)驗(yàn)在內(nèi)核空間處理Guest退去。函數(shù)kvm_handle_exit有個(gè)商定,假如在內(nèi)核空間可以告捷處理假造機(jī)退去,大概是由于別的干擾好比外部中綴招致假造機(jī)退去等無須切換到Host的用戶空間,則前往1;
不然前往0,表現(xiàn)必要告急KVM的用戶空間處理假造機(jī)退去,好比必要KVM用戶空間的模仿裝備處理外設(shè)哀求。
假如內(nèi)核空間告捷處理了假造機(jī)的退去,則函數(shù)kvm_handle_exit前往1,在上述代碼中即直接跳轉(zhuǎn)到標(biāo)簽again處,然后步驟流程會(huì)再次切入Guest。
假如函數(shù)kvm_handle_exit前往0,則函數(shù)vmx_vcpu_run完畢實(shí)行,CPU從內(nèi)核空間前往到用戶空間,以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:
…
}
…
}
依據(jù)代碼可見,kvmtool倡導(dǎo)進(jìn)入Guest的代碼處于一個(gè)for的無窮循環(huán)中。當(dāng)從KVM內(nèi)核空間前往用戶空間后,kvmtool在用戶空間處理Guest的哀求,好比調(diào)用模仿裝備處理I/O哀求。
在處理完Guest的哀求后,重新進(jìn)入下一輪for循環(huán),kvmtool再次哀求KVM模塊切入Guest。
福利
CSDN 攜手【機(jī)器產(chǎn)業(yè)出書社】送出
《深度探究Linux體系假造化:原理與完成》一本
停止11月25日18:00點(diǎn)
作者簡介:
王柏生
資深武藝專家,先后就職于中科院軟件所、紅旗Linux和百度,現(xiàn)任百度主任架構(gòu)師。在利用體系、假造化武藝、分布式體系、云盤算、主動(dòng)駕駛等干系范疇耕作多年,有著豐厚的實(shí)踐履歷。
著有熱銷書《深度探究Linux利用體系》(2013年出書)。
謝廣軍
盤算機(jī)專業(yè)博士,畢業(yè)于南開大學(xué)盤算機(jī)系。
資深武藝專家,有多年的IT行業(yè)事情履歷。現(xiàn)承繼百度智能云副總司理,賣力云盤算干系產(chǎn)物的研發(fā)。多年來不休從事利用體系、假造化武藝、分布式體系、大數(shù)據(jù)、云盤算等干系范疇的研發(fā)事情,實(shí)踐履歷豐厚。
版權(quán)聲明:本文來自互聯(lián)網(wǎng)整理發(fā)布,如有侵權(quán),聯(lián)系刪除
原文鏈接:http://www.freetextsend.comhttp://www.freetextsend.com/qingganjiaoliu/34326.html