1.主線程初始化
- qemu層kvm初始化類
//KVM初始化的入口的類
static const TypeInfo kvm_accel_type = {
.name = TYPE_KVM_ACCEL,
.parent = TYPE_ACCEL,
.instance_init = kvm_accel_instance_init,
.class_init = kvm_accel_class_init,
.instance_size = sizeof(KVMState),
};
說明:
1.QOM模型,先執行class_init初始化函數,類似C++中構造函數,在new對象的時候調用
2.QOM模型,instance_finalize實例化函數,本類的具體實現
- 主線程kvm初始化
|-qemu_init
|-module_call_init(MODULE_INIT_QOM); //vl.c
|-kvm_type_init //初始化注冊kvm類類型
|-configure_accelerators //vl.c
|-do_configure_accelerator //設置為kvm加速,比如有hax、hvf、tcg、kvm、xen、whpx不同加速等
|-accel_init_machine
|-acc->init_machine(ms); //調用kvm_init接口,這個是kvm真正初始化的入口
|-kvm_init //kvm-all.c
|-machine_run_board_init //主板初始化,此時運行狀態即將設置為running或者prelaunch
|-machine_class->init
|-pc_init1 //i386/pc_piix.c 進行CPU、Memory、VGA、NIC、PCI等的初始化
|-x86_cpus_init //x86的CPU初始化---cpu虛擬化
|-pc_guest_info_init //guest信息初始化
|-smbios_set_defaults //bios設置
|-pc_memory_init //分配內存和加載rom和bios --內存虛擬化
|-pc_gsi_create //中斷控制器
|-i440fx_init //初始化pci總線
|-piix3_create //初始化isa總線
|-pc_i8259_create //中斷控制器初始化
|-pc_vga_init //顯卡初始化
|-pc_basic_device_init //基本硬件初始化
|-pc_nic_init //網卡初始化
|-pci_create_simple //創建pci主橋
關注的兩個熱點:
1.x86_cpus_init //cpu虛擬化
2.pc_memory_init //內存虛擬化
- 調用堆棧
(gdb) bt
#0 pc_init1 (machine=0x555556e39800, pci_type=0x555556164c06 "i440FX", host_type=0x555556163b61 "i440FX-pcihost") at /home/cloud/qemu/qemu-5.0.0/hw/i386/pc_piix.c:74
#1 0x0000555555a4362b in machine_run_board_init (machine=0x555556e39800) at hw/core/machine.c:1140
#2 0x00005555559749ca in qemu_init (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/vl.c:4897
#3 0x000055555585cd19 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/main.c:48
2.Qemu層vcpu初始化
- x86_vcpu初始化類
static const TypeInfo x86_cpu_type_info = {
.name = TYPE_X86_CPU,
.parent = TYPE_CPU,
.instance_size = sizeof(X86CPU),
.instance_init = x86_cpu_initfn,
.abstract = true,
.class_size = sizeof(X86CPUClass),
.class_init = x86_cpu_common_class_init,
};
x86_cpu類型注冊:
|-type_init //初始化的時候注冊,main函數運行前
|-x86_cpu_register_types
|-x86_cpu_type_info //class_init類似構造函數
|-x86_cpu_common_class_init
|-device_class_set_parent_realize //設置cpu初始化函數realize=x86_cpu_realizefn
|-qemu_init_vcpu //開始初始化vcpu
- vcpu初始化邏輯
|-x86_cpus_init
|-x86_cpu_new //CPUState *cs = CPU(dev);
|-object_new
|-object_new_with_type
|-type_initialize
|-object_initialize_with_type
|-type_initialize
|-class_init //調用x86_cpu_type_info的x86_cpu_common_class_init初始化x86_cpu
|-object_property_set_bool
|-object_property_set_qobject
|-property_set_bool
|-device_set_realized
|-x86_cpu_realizefn //vcpu初始化的實現,先初始化struct X86CPU再初始化struct CPUState
|-qemu_init_vcpu //struct CPUState
|-qemu_kvm_start_vcpu
|-qemu_kvm_cpu_thread_fn
|-kvm_init_vcpu
|-kvm_get_vcpu //通知kvm創建一個vcpu,返回一個kvm_fd
|-kvm_ioctl //從kvm獲取映射mmap_size
|-mmap //對kvm_fd進行以一個mmap_size大小映射
|-kvm_arch_init_vcpu //前面vcpu創建成功之后現在來真正的初始化
|-kvm_arch_set_tsc_khz //時鐘設置
|-kvm_vcpu_ioctl //KVM_GET_TSC_KHZ時鐘可以用戶設置和從kvm獲取,這里從KVM獲取再保存下來
|-hyperv_handle_properties //設置cpuids
|-cpu_x86_cpuid //獲取所有的CPUID信息,填充結構體,設置到寄存器中
|-kvm_vcpu_ioctl //前面構造KVM需要的cpuid_data數據,再通過KVM_SET_CPUID2給kvm設置cpuid
|-kvm_init_msrs //通知kvm設置msr寄存器
|-hyperv_init_vcpu //Hyper-V初始化vcpu保存一些信息在后續遷移時候恢復使用
|-kvm_init_cpu_signals
|-kvm_cpu_exec //每個vcpu對應一個線程,該線程循環執行
x86_cpu類繼承關系:子類->父類
struct X86CPU -> struct CPUState -> struct DeviceState
struct X86CPUClass -> struct CPUClass
struct CPUX86State -> (struct X86CPU env)
DeviceClass 可以通過DeviceState獲取得到DEVICE_GET_CLASS(dev)。
說明:
1.這里x86_cpu_new調用比較抽象,涉及QOM
2.這里cpu虛擬的具體流程還要結合kvm的實現才能分析透徹
3.先初始化父類再初始化子類,先初始化Class,再初始化Device,再初始化State如先初始化x86CPU數據結構,再去針對CPUState進行初始化,CPUState初始化結合KVM完成。CPUState記錄的是和kvm交互的狀態信息,CPUX86State記錄的是一些寄存器、cpu狀態之類信息。
- 調用堆棧
(gdb) bt
#0 x86_cpu_realizefn (dev=0x5555572bb6c0, errp=0x7fffffffca30) at /home/cloud/qemu/qemu-5.0.0/target/i386/cpu.c:6528
#1 0x0000555555a3ab11 in device_set_realized (obj=<optimized out>, value=<optimized out>, errp=0x7fffffffcb20) at hw/core/qdev.c:891
#2 0x0000555555bd744e in property_set_bool (obj=0x5555572bb6c0, v=<optimized out>, name=<optimized out>, opaque=0x555556c71020, errp=0x7fffffffcb20) at qom/object.c:2243
#3 0x0000555555bdc10f in object_property_set_qobject (obj=obj@entry=0x5555572bb6c0, value=value@entry=0x5555572d68d0, name=name@entry=0x55555619ea48 "realized", errp=errp@entry=0x7fffffffcb20) at qom/qom-qobject.c:26
#4 0x0000555555bd98d5 in object_property_set_bool (obj=0x5555572bb6c0, value=<optimized out>, name=0x55555619ea48 "realized", errp=0x7fffffffcb20) at qom/object.c:1395
#5 0x0000555555944ebf in x86_cpu_new (x86ms=x86ms@entry=0x555556e39850, apic_id=0, errp=0x555556b7e6b0 <error_fatal>) at /home/cloud/qemu/qemu-5.0.0/hw/i386/x86.c:129
#6 0x0000555555944ff9 in x86_cpus_init (x86ms=0x555556e39850, default_cpu_version=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/hw/i386/x86.c:168
#7 0x000055555594c2b9 in pc_init1 (machine=0x555556e39850, pci_type=0x555556164c06 "i440FX", host_type=0x555556163b61 "i440FX-pcihost") at /home/cloud/qemu/qemu-5.0.0/hw/i386/pc_piix.c:156
#8 0x0000555555a4362b in machine_run_board_init (machine=0x555556e39850) at hw/core/machine.c:1140
#9 0x00005555559749ca in qemu_init (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/vl.c:4897
#10 0x000055555585cd19 in main (argc=<optimized out>, argv=<optimized out>, envp=<optimized out>) at /home/cloud/qemu/qemu-5.0.0/softmmu/main.c:48
3.VCPU運行
由于虛擬機涉及VM-Entey和VM-exit,這里從Qemu和KVM角度來分析vcpu運行主要機制。
- Qemu
|-kvm_cpu_exec
|-kvm_arch_process_async_events
|—cpu_exec_start
|-kvm_arch_pre_run
|-kvm_vcpu_ioctl //ioctl下發KVM_RUN給KVM,進入了VM-Entry模式
|-kvm_arch_post_run
|-switch (run->exit_reason) //處理VM-Exit事件
|-kvm_handle_io
|-kvm_handle_cpuid
|-kvm_arch_handle_exit
|-......
|-kvm_arch_handle_exit
|-cpu_exec_end
1.kvm_cpu_exec中能看到一個循環,在循環中kvm_vcpu_ioctl(KVM_RUN)運行這個虛擬機,這個時候CPU進入VM-Entry即進入客戶機模式。
2.如果一直是客戶機的操作系統占用這個CPU,則會一直停留在這一行運行,一旦這個調用返回了,就說明CPU進入VM-Exit退出客戶機模式。
3.退出客戶機模式之后將CPU交還給宿主機。在循環中會對退出的原因exit_reason進行分析處理,是因為有了I/O、還是有了中斷等,并做相應的處理。處理完畢之后再次循環,再次通過VM-Entry進入客戶機模式,如此循環直到虛擬機正常或者異常退出。
- KVM
KVM: //virt/kvm/kvm_main.c
|-kvm_vcpu_ioctl
|-kvm_arch_vcpu_ioctl_run //arch/x86/kvm/x86.c
|-vcpu_run
|-vcpu_enter_guest
|- 判斷是否需要退出,如果退出則設置退出的reason
|-kvm_x86_ops->prepare_guest_switch
|-kvm_x86_ops->run //進入guest模式
|-kvm_x86_ops->handle_exit //處理eixt,可能要退出guest模式
vcpu運行函數:
static int vcpu_run(struct kvm_vcpu *vcpu)
{
int r;
struct kvm *kvm = vcpu->kvm;
...
for (;;) {
if (kvm_vcpu_running(vcpu)) {
r = vcpu_enter_guest(vcpu);
} else {
r = vcpu_block(kvm, vcpu);
}
...
}
}
x86架構操作函數集://arch/x86/kvm/vmx/vmx.c
static struct kvm_x86_ops vmx_x86_ops __ro_after_init = {
...
.run = vmx_vcpu_run,
.handle_exit = vmx_handle_exit,
...
}
1.首先是Store host registers,要從宿主機模式變為客戶機模式了,所以原來宿主機運行時候的寄存器要保存下來。
2.接下來是Load guest registers,將原來客戶機運行時的寄存器加載進來。
3.接下來是Enter guest mode,調用ASM_VMX_VMLAUNCH進入客戶機模型運行,或者ASM_VMX_VMRESUME恢復客戶機模型運行。
4.如果客戶機因為某種原因退出,Save guest registers, load host registers,即保存客戶機運行的時候的寄存器,就加載宿主機運行的時候的寄存器。
4.參考資料
各種虛擬化:https://blog.csdn.net/qq_33588730/article/details/105397879
浙公網安備 33010602011771號