kvm和qemu交互處理io流程


1、IO虛擬化的分類 

(1)全虛擬化:宿主機截獲客戶機對I/O設備的訪問請求,然后通過軟件模擬真實的硬件。這種方式對客戶機而言非常透明,無需考慮底層硬件的情況,不需要修改操作系統。 

QEMU模擬I/O的情況下,當客戶機中的設備驅動程序(device driver)發起I/O操作請求之時,KVM模塊中的I/O操作捕獲代碼會攔截這次I/O請求,然后經過處理后將本次I/O請求的信息存放到I/O共享頁,並通知用戶控件的QEMU程序。QEMU模擬程序獲得I/O操作的具體信息之后,交由硬件模擬代碼來模擬出本次的I/O操作,完成之后,將結果放回到I/O共享頁,並通知KVM模塊中的I/O操作捕獲代碼。最后,由KVM模塊中的捕獲代碼讀取I/O共享頁中的操作結果,並把結果返回到客戶機中。當然,這個操作過程中客戶機作為一個QEMU進程在等待I/O時也可能被阻塞。

另外,當客戶機通過DMADirect Memory Access【DMA外接設備可以不用CPU干預,直接把數據傳輸到內存的技術,盡量減少CPU干預的輸入/輸出操作方式。(使用連續物理內存,kmalloc分配)。否則外設一有數據就要中斷通知CPU,CPU去讀,如果頻繁就時間浪費在處理中斷,IO速度慢】訪問大塊I/O之時,QEMU模擬程序將不會把操作結果放到I/O共享頁中,而是通過內存映射的方式將結果直接寫到客戶機的內存中去,然后通過KVM模塊告訴客戶機DMA操作已經完成。 

  • 優點是可以模擬出各種各樣的硬件設備;
  • 缺點是每次 I/O 操作的路徑比較長,需要多次上下文切換、多次數據復制和VM-Entry/Exit,性能較差。 

(2)半虛擬化:通過前端驅動/后端驅動模擬實現I/O虛擬化。客戶機中的驅動程序virtio-blk/net/pci/scsi為前端,宿主機提供的與客戶機通信的驅動程序vhost為后端。前后端驅動通過 virtio-ring 直接通信,繞過了經過 KVM 內核模塊的過程,達到提高 I/O 性能的目的。 

  • 優點快。vring實現了環形緩沖區(ring buffer),用於保存前端驅動和后端處理程序執行的信息,並且它可以一次性保存前端驅動的多次I/O請求,並且交由后端去動去批量處理,最后實際調用宿主機中設備驅動實現物理上的I/O操作,這樣做就可以根據約定實現批量處理而不是客戶機中每次I/O請求都需要處理一次,從而提高客戶機與hypervisor信息交換的效率。
  • 缺點:需要客戶機中virtio相關驅動的支持(較老的系統默認沒有自帶這些驅動,Windows系統中需要額外安裝virtio驅動),故兼容性較差,而且I/O頻繁時的CPU使用率較高 

(3)IO透傳:直接把物理設備分配給虛擬機使用,這種方式需要硬件平台具備I/O透傳技術,例如Intel VT-d技術。它能獲得近乎本地的性能,並且CPU開銷不高。

  

2、kvm和qemu的交互

Qemu創建虛擬機進入kvm:main函數通過調用kvm_init 和 machine->init來初始化kvm. 其中, machine->init會創建vcpu, 用一個線程去模擬vcpu, 該線程執行的函數為qemu_kvm_cpu_thread_fn, 並且該線程最終kvm_cpu_exec,該函數調用kvm_vcpu_ioctl切換到kvm中。

Kvm運行並因io退出:在kvm中看到參數KVM_RUN,最后調用vcpu_enter_guest,然后 vmx_vcpu_run設置好寄存器狀態之后調用VM_LAUNCH或者VM_RESUME進入guest vm。如果vm進行IO操作需要訪問設備時,就會觸發vm exit 返回到vmx_vcpu_run, vmx保存好vmcs並且記錄下VM_EXIT_REASON后返回到調用該函數的vcpu_enter_guest, 在vcpu_enter_guest函數末尾調用了r = kvm_x86_ops->handle_exit(vcpu), 該函數對應於vmx_handle_exit函數, vmx_handle_exit 調用kvm_vmx_exit_handlers[exit_reason](vcpu),該語句根據exit_reason調用不同的函數。io操作則是handle_io把數據填充到vcpu->run,就一路return到kvm_vcpu_ioctl,就ioctl返回到qemu的kvm_cpu_exec中。

從kvm返回到qemu后的處理:Qemu在kvm_cpu_exec中會看kvm_run的run->exit_reason如果是KVM_EXIT_IO就進入kvm_handle_io里處理。 當qemu完成IO操作后,會在kvm_cpu_exec函數的循環中,調用kvm_vcpu_ioctl重新進入kvm。

kvm_run,這是用於vcpu和應用層的程序(典型如qemu)通信的一個結構,user space的 程序通過KVM__VCPU_MMAP_SIZE這個ioctl得到大小,然后映射到用戶空間。

 

3、kvm的io處理流程

static int handle_io(struct kvm_vcpu *vcpu)

{

         unsigned long exit_qualification;

         int size, in, string;

         unsigned port;

 

         exit_qualification = vmcs_readl(EXIT_QUALIFICATION);  //獲取exit qualification

         string = (exit_qualification & 16) != 0; //判斷是否為string io (ins, outs)

         in = (exit_qualification & 8) != 0; //判斷io方向,是in  還是out

 

         ++vcpu->stat.io_exits;

 

         if (string || in) //如果是輸入類的指令,或者是string io,就進入emulator處理

                   return emulate_instruction(vcpu, 0) == EMULATE_DONE;

 

         port = exit_qualification >> 16; //得到端口號

         size = (exit_qualification & 7) + 1; //大小

         skip_emulated_instruction(vcpu); //跳過這個指令

 

         return kvm_fast_pio_out(vcpu, size, port); //進行out操作

}

Guest執行io指令 -> 發生vmexit-> 返回qemu -> 處理io

1、out指令虛擬:虛擬單個out指令,在KVM中可以直接把out的數據返回給qemuqemu完成out操作

流程:KVM的handle_io->kvm_fast_pio_out->emulator_pio_out_emulated后面是vcpu->arch.pio.count = 0函數中非string類型的 out操作可以一步完成,所以從qemu處理完返回kvm后不需要再進入emulator。在emulator_pio_out_emulated中,將IO數據memcpy到kvm和qemu共享buffer中,然后emulator_pio_in_out,將相應數據保存到kvm_run中就返回到qemu的kvm_cpu_exec的switch看run->exit_reason,如果是KVM_EXIT_IO則進入kvm_handle_io中和設備交互。

2、Stringin指令虛擬:如果是in指令,qemu只能把得到的數據寫到kvm_run中,kvm必須在下一次vmentry的時候,將qemu得到的數據放到相應的位置,所以,在handle_io中,如果是in或者string指令,沒有調用skip_emulated_instruction,這樣,qemu完成in或者一次out之后,還會在同樣的地方發生vmexit,這樣再由emulator完成相應的處理,針對string類型的指令,emulator會進行解碼等操作,確認io的次數和源操作數、目的操作數等。

流程:handle_io->emulate_instruction->x86_emulate_instruction對指令的decode,在過程中會調用到em_in和em_out(這兩個函數最后調用的emulator_pio_in_emulated中先通過和上面PIO一樣的函數emulator_pio_in_out,正確返回表明qemu已經將模擬出的數據返回到參數val了,則可直接memcpy完成具體的將從qemu中得到的數據寫到正確位置vcpu->arch.pio_data),設置如果是out,下次到KVM時直接進入emulator,如果是in,注冊vcpu->arch.complete_userspace_io = complete_emulated_pio;需要在下次qemu進入kvm的時候,完成io,實際上就是將qemu得到的數據寫到正確的位置。下次進入kvm,如果要完成in指令,會在函數kvm_arch_vcpu_ioctl_run中調用注冊的complete_emulated_pio會再次調用emulate_instruction將數據寫到正確位置(這次不用解碼,而是直接em_in)。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM