ernel 3.10核心原始碼分析--KVM相關--虛擬機器執行
阿新 • • 發佈:2019-01-23
1、基本原理
KVM虛擬機器通過字元裝置/dev/kvm的ioctl介面建立和執行,相關原理見之前的文章說明。
虛擬機器的執行通過/dev/kvm裝置ioctl VCPU介面的KVM_RUN指令實現,在VM和VCPU建立好並完成初始化後,就可以排程該虛擬機器運行了,通常,一個VCPU對應於一個執行緒,虛擬機器執行的本質為排程該虛擬機器相關的VCPU所線上程執行。虛擬機器(VCPU)的執行主要任務是要進行上下文切換,上下文主要包括相關暫存器、APIC狀態、TLB等,通常上下文切換的過程如下:
1、儲存當前的上下文。
2、使用kvm_vcpu結構體中的上下文資訊,載入到物理CPU中。
3、執行kvm_x86_ops 中的run_vcpu函式,呼叫硬體相關的指令(如VMLAUNCH),進入虛擬機器執行環境中。
虛擬機器運行於qemu-kvm的程序上下文中,從硬體的角度看,虛擬機器的執行過程,實質為相關指令的執行過程,虛擬機器編譯後的也就是相應的CPU指令序列,而虛擬機器的指令跟Host機的指令執行過程並沒有太多的差別,最關鍵的差別為“敏感指令”(通常為IO、記憶體等關鍵操作)的執行,這也是虛擬化實現的本質所在,當在虛擬機器中(Guest模式)執行“敏感指令”時,會觸發(由硬體觸發)VM-exit,使當前CPU從Guest模式(non-root模式)切換到root模式,當前CPU的控制權隨之轉交給VMM(Hypervisor,KVM中即Host),由VMM進行相應的處理,處理完成後再次通過應該硬體指令 (如VMLAUNCH),重新進入到Guest模式,從而進入虛擬機器執行環境中繼續執行。
本文簡單解釋及分析在3.10版本核心程式碼中的相關流程,使用者態qemu-kvm部分暫不包括。
2、大致流程:
Qemu-kvm可以通過ioctl(KVM_RUN…)使虛擬機器執行,最終進入核心態,由KVM相關核心流程處理,在核心態執行的大致過程如下:
kvm_vcpu_ioctl -->
kvm_arch_vcpu_ioctl_run
具體由核心函式kvm_arch_vcpu_ioctl_run完成相關工作。主要流程如下:
3、程式碼分析
kvm_vcpu_ioctl():
kvm_vcpu_ioctl()-->kvm_arch_vcpu_ioctl_run()-->__vcpu_run():
KVM虛擬機器通過字元裝置/dev/kvm的ioctl介面建立和執行,相關原理見之前的文章說明。
虛擬機器的執行通過/dev/kvm裝置ioctl VCPU介面的KVM_RUN指令實現,在VM和VCPU建立好並完成初始化後,就可以排程該虛擬機器運行了,通常,一個VCPU對應於一個執行緒,虛擬機器執行的本質為排程該虛擬機器相關的VCPU所線上程執行。虛擬機器(VCPU)的執行主要任務是要進行上下文切換,上下文主要包括相關暫存器、APIC狀態、TLB等,通常上下文切換的過程如下:
1、儲存當前的上下文。
2、使用kvm_vcpu結構體中的上下文資訊,載入到物理CPU中。
3、執行kvm_x86_ops
虛擬機器運行於qemu-kvm的程序上下文中,從硬體的角度看,虛擬機器的執行過程,實質為相關指令的執行過程,虛擬機器編譯後的也就是相應的CPU指令序列,而虛擬機器的指令跟Host機的指令執行過程並沒有太多的差別,最關鍵的差別為“敏感指令”(通常為IO、記憶體等關鍵操作)的執行,這也是虛擬化實現的本質所在,當在虛擬機器中(Guest模式)執行“敏感指令”時,會觸發(由硬體觸發)VM-exit,使當前CPU從Guest模式(non-root模式)切換到root模式,當前CPU的控制權隨之轉交給VMM(Hypervisor,KVM中即Host),由VMM進行相應的處理,處理完成後再次通過應該硬體指令
本文簡單解釋及分析在3.10版本核心程式碼中的相關流程,使用者態qemu-kvm部分暫不包括。
2、大致流程:
Qemu-kvm可以通過ioctl(KVM_RUN…)使虛擬機器執行,最終進入核心態,由KVM相關核心流程處理,在核心態執行的大致過程如下:
kvm_vcpu_ioctl -->
kvm_arch_vcpu_ioctl_run
具體由核心函式kvm_arch_vcpu_ioctl_run完成相關工作。主要流程如下:
1、Sigprocmask()遮蔽訊號,防止在此過程中受到訊號的干擾。
2、設定當前VCPU狀態為KVM_MP_STATE_UNINITIALIZED
3、配置APIC和mmio相關資訊
4、將VCPU中儲存的上下文資訊寫入指定位置
5、然後的工作交由__vcpu_run完成
6、__vcpu_run最終呼叫vcpu_enter_guest,該函式實現了進入Guest,並執行Guest OS具體指令的操作。
7、vcpu_enter_guest最終呼叫kvm_x86_ops中的run函式執行。對應於Intel平臺,該函式為vmx_vcpu_run(設定Guest CR3和其他暫存器、EPT/影子頁表相關設定、彙編程式碼VMLAUNCH切換到非根模式,執行Guest目的碼)。
8、Guest程式碼執行到敏感指令或因其他原因(比如中斷/異常),VM-Exit退出非根模式,返回到vcpu_enter_guest函式繼續執行。
9、vcpu_enter_guest函式中會判斷VM-Exit原因,並進行相應處理。
10、處理完成後VM-Entry到Guest重新執行Guest程式碼,或重新等待下次排程。
3、程式碼分析
kvm_vcpu_ioctl():
點選(此處)摺疊或開啟
-
/*
-
* kvm
ioctl VCPU指令的入口,傳入的fd為KVM_CREATE_VCPU中返回的fd。
-
* 主要針對具體的VCPU進行引數設定。如:相關暫存器的讀
-
* 寫、中斷控制等
-
*/
-
static long kvm_vcpu_ioctl(struct file *filp,
-
unsigned int ioctl, unsigned
long arg)
-
{
-
struct kvm_vcpu *vcpu = filp->private_data;
-
void __user *argp = (void
__user *)arg;
-
int r;
-
struct kvm_fpu *fpu = NULL;
-
struct kvm_sregs *kvm_sregs = NULL;
-
if (vcpu->kvm->mm != current->mm)
-
return -EIO;
-
#if defined(CONFIG_S390) || defined(CONFIG_PPC) || defined(CONFIG_MIPS)
-
/*
-
* Special
cases: vcpu ioctls that are asynchronous to vcpu execution,
-
* so
vcpu_load() would
break it.
-
*/
-
if (ioctl == KVM_S390_INTERRUPT || ioctl == KVM_INTERRUPT)
-
return kvm_arch_vcpu_ioctl(filp, ioctl, arg);
-
#endif
-
// KVM虛擬機器VCPU資料結構載入物理CPU
-
r = vcpu_load(vcpu);
-
if (r)
-
return r;
-
switch (ioctl) {
-
/*
-
* 執行虛擬機器,最終通過執行VMLAUNCH指令進入non
root模式,
-
* 進入虛擬機器執行。當虛擬機器內部執行敏感指令時,由硬
-
* 件觸發VM-exit,返回到root模式
-
*/
-
case KVM_RUN:
-
r = -EINVAL;
-
// 不能帶引數。
-
if (arg)
-
goto out;
-
// 執行VCPU(即執行虛擬機器)的入口函式
-
r = kvm_arch_vcpu_ioctl_run(vcpu, vcpu->run);
-
trace_kvm_userspace_exit(vcpu->run->exit_reason, r);
-
break;
- ...
kvm_vcpu_ioctl()-->kvm_arch_vcpu_ioctl_run()-->__vcpu_run():
點選(此處)摺疊或開啟
-
static int __vcpu_run(struct
kvm_vcpu *vcpu)
-
{
-
int r;
-
struct kvm *kvm = vcpu->kvm;
-
vcpu->srcu_idx = srcu_read_lock(&kvm->srcu);
-
/*設定vcpu->arch.apic->vapic_page*/
-
r = vapic_enter(vcpu);
-
if (r) {
-
srcu_read_unlock(&kvm->srcu, vcpu->srcu_idx);
-
return r;
-
}
-
r = 1;
-
while (r > 0) {
-
/*檢查狀態*/
-
if (vcpu->arch.mp_state == KVM_MP_STATE_RUNNABLE &&
-
!vcpu->arch.apf.halted)
- /* 進入Guest模式,最終通過VMLAUNCH指令實現