1. 程式人生 > >KVM(三)CPU 和記憶體虛擬化

KVM(三)CPU 和記憶體虛擬化

在 QEMU/KVM 中,客戶機可以使用的裝置大致可分為三類:

1. 模擬裝置:完全由 QEMU 純軟體模擬的裝置。

2. Virtio 裝置:實現 VIRTIO API 的半虛擬化裝置。

3. PCI 裝置直接分配 (PCI device assignment) 。

1. 全虛擬化 I/O 裝置

KVM 在 IO 虛擬化方面,傳統或者預設的方式是使用 QEMU 純軟體的方式來模擬 I/O 裝置,包括鍵盤、滑鼠、顯示器,硬碟 和 網絡卡 等。模擬裝置可能會使用物理的裝置,或者使用純軟體來模擬。模擬裝置只存在於軟體中。 

1.1 原理

過程:

  1. 客戶機的裝置驅動程式發起 I/O 請求操作請求
  2. KVM 模組中的 I/O 操作捕獲程式碼攔截這次 I/O 請求
  3. 經過處理後將本次 I/O 請求的資訊放到 I/O 共享頁 (sharing page),並通知使用者空間的 QEMU 程式。
  4. QEMU 程式獲得 I/O 操作的具體資訊之後,交由硬體模擬程式碼來模擬出本次 I/O 操作。
  5. 完成之後,QEMU 將結果放回 I/O 共享頁,並通知 KMV 模組中的 I/O 操作捕獲程式碼。
  6. KVM 模組的捕獲程式碼讀取 I/O 共享頁中的操作結果,並把結果放回客戶機。 

注意:當客戶機通過DMA (Direct Memory Access)訪問大塊I/O時,QEMU 模擬程式將不會把結果放進共享頁中,而是通過記憶體對映的方式將結果直接寫到客戶機的記憶體中共,然後通知KVM模組告訴客戶機DMA操作已經完成。

這種方式的優點是可以模擬出各種各樣的硬體裝置;其缺點是每次 I/O 操作的路徑比較長,需要多次上下文切換,也需要多次資料複製,所以效能較差。 

1.2 QEMU 模擬網絡卡的實現

Qemu 純軟體的方式來模擬I/O裝置,其中包括經常使用的網絡卡裝置。Guest OS啟動命令中沒有傳入的網路配置時,QEMU預設分配 rtl8139 型別的虛擬網絡卡型別,使用的是預設使用者配置模式,這時候由於沒有具體的網路模式的配置,Guest的網路功能是有限的。 全虛擬化情況下,KVM虛機可以選擇的網路模式包括:

  1. 預設使用者模式(User);
  2. 基於網橋(Bridge)的模式;
  3. 基於NAT(Network Address Translation)的模式;

分別使用的 qemu-kvm 引數為:

  • -net user[,vlan=n]:使用使用者模式網路堆疊,這樣就不需要管理員許可權來執行.如果沒有指 定-net選項,這將是預設的情況.-net tap[,vlan=n][,fd=h]
  • -net nic[,vlan=n][,macaddr=addr]:建立一個新的網絡卡並與VLAN n(在預設的情況下n=0)進行連線。作為可選項的專案,MAC地址可以進行改變.如果 沒有指定-net選項,則會建立一個單一的NIC.
  • -net tap[,vlan=n][,fd=h][,ifname=name][,script=file]:將TAP網路介面 name 與 VLAN n 進行連線,並使用網路配置指令碼檔案進行 配置。預設的網路配置指令碼為/etc/qemu-ifup。如果沒有指定name,OS 將會自動指定一個。fd=h可以用來指定一個已經開啟的TAP主機介面的控制代碼。

網橋模式是目前比較簡單,也是用的比較多的模式,下圖是網橋模式下的 VM的收發包的流程。

如圖中所示,紅色箭頭表示資料報文的入方向,步驟:

  1. 網路資料從 Host 上的物理網絡卡接收,到達網橋;
  2. 由於 eth0 與 tap1 均加入網橋中,根據二層轉發原則,br0 將資料從 tap1 口轉發出去,即資料由 Tap裝置接收;
  3. Tap 裝置通知對應的 fd 資料可讀;
  4. fd 的讀動作通過 tap 裝置的字元裝置驅動將資料拷貝到使用者空間,完成資料報文的前端接收。

(引用自 http://luoye.me/2014/07/17/netdev-virtual-1/

 1.3 RedHat Linux 6 中提供的模擬裝置

  • 模擬顯示卡:提供2塊模擬顯示卡。
  • 系統元件:
    • ntel i440FX host PCI bridge
    • PIIX3 PCI to ISA bridge
    • PS/2 mouse and keyboard
    • EvTouch USB Graphics Tablet
    • PCI UHCI USB controller and a virtualized USB hub
    • Emulated serial ports
    • EHCI controller, virtualized USB storage and a USB mouse
  • 模擬的音效卡:intel-hda
  • 模擬網絡卡:e1000,模擬 Intel E1000 網絡卡;rtl8139,模擬 RealTeck 8139 網絡卡。
  • 模擬儲存卡:兩塊模擬 PCI IDE 介面卡。KVM  限制每個虛擬機器最多隻能有4塊虛擬儲存卡。還有模擬軟碟機。

注意:RedHat Linux KVM 不支援 SCSI 模擬。

在不顯式指定使用其它型別裝置的情況下,KVM 虛機將使用這些預設的虛擬裝置。比如上面描述的預設情況下 KVM 虛機預設使用rtl8139網絡卡。比如,在 RedHat Linxu 6.5 主機上啟動KVM RedHat Linux 6.4 虛機後,登入虛機,檢視 pci 裝置,可以看到這些模擬裝置:

 

當使用 “-net nic,model=e1000” 指定網絡卡model 為 e1000 時,

 

1.4 qemu-kvm 關於磁碟裝置和網路的主要選項

型別 選項
磁碟裝置(軟盤、硬碟、CDROM等)
  1.   -drive option[,option[,option[,...]]]:定義一個硬碟裝置;可用子選項有很多。
  2.   file=/path/to/somefile:硬體映像檔案路徑;
  3.   if=interface:指定硬碟裝置所連線的介面型別,即控制器型別,如ide、scsi、sd、mtd、floppy、pflash及virtio等;
  4.   index=index:設定同一種控制器型別中不同裝置的索引號,即標識號;
  5.   media=media:定義介質型別為硬碟(disk)還是光碟(cdrom); format=format:指定映像檔案的格式,具體格式可參見qemu-img命令;
  6.   -boot [order=drives][,once=drives][,menu=on|off]:定義啟動裝置的引導次序,每種裝置使用一個字元表示;不同的架構所支援的裝置及其表示字元不盡相同,在x86 PC架構上,a、b表示軟碟機、c表示第一塊硬碟,d表示第一個光碟機裝置,n-p表示網路介面卡;預設為硬碟裝置(-boot order=dc,once=d)
網路

-net nic[,vlan=n][,macaddr=mac][,model=type][,name=name][,addr=addr][,vectors=v]:建立一個新的網絡卡裝置並連線至vlan n中;PC架構上預設的NIC為e1000,macaddr用於為其指定MAC地址,name用於指定一個在監控時顯示的網上裝置名稱;emu可以模擬多個型別的網絡卡裝置;可以使用“qemu-kvm -net nic,model=?”來獲取當前平臺支援的型別;

-net tap[,vlan=n][,name=name][,fd=h][,ifname=name][,script=file][,downscript=dfile]:通過物理機的TAP網路介面連線至vlan n中,使用script=file指定的指令碼(預設為/etc/qemu-ifup)來配置當前網路介面,並使用downscript=file指定的指令碼(預設為/etc/qemu-ifdown)來撤消介面配置;使用script=no和downscript=no可分別用來禁止執行指令碼;

-net user[,option][,option][,...]:在使用者模式配置網路棧,其不依賴於管理許可權;有效選項有:

    vlan=n:連線至vlan n,預設n=0;

    name=name:指定介面的顯示名稱,常用於監控模式中;

    net=addr[/mask]:設定GuestOS可見的IP網路,掩碼可選,預設為10.0.2.0/8;

    host=addr:指定GuestOS中看到的物理機的IP地址,預設為指定網路中的第二個,即x.x.x.2;

    dhcpstart=addr:指定DHCP服務地址池中16個地址的起始IP,預設為第16個至第31個,即x.x.x.16-x.x.x.31;

    dns=addr:指定GuestOS可見的dns伺服器地址;預設為GuestOS網路中的第三個地址,即x.x.x.3;

    tftp=dir:啟用內建的tftp伺服器,並使用指定的dir作為tftp伺服器的預設根目錄;

    bootfile=file:BOOTP檔名稱,用於實現網路引導GuestOS;如:qemu -hda linux.img -boot n -net user,tftp=/tftpserver/pub,bootfile=/pxelinux.0

 對於網絡卡來說,你可以使用 modle 引數指定虛擬網路的型別。 RedHat Linux 6 所支援的虛擬網路型別有:

[[email protected] isoimages]# kvm -net nic,model=? qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio 

2. 準虛擬化 (Para-virtualizaiton) I/O 驅動 virtio

在 KVM 中可以使用準虛擬化驅動來提供客戶機的I/O 效能。目前 KVM 採用的的是 virtio 這個 Linux 上的裝置驅動標準框架,它提供了一種 Host 與 Guest 互動的 IO 框架。

2.1 virtio 的架構

 KVM/QEMU 的 vitio 實現採用在 Guest OS 核心中安裝前端驅動 (Front-end driver)和在 QEMU 中實現後端驅動(Back-end)的方式。前後端驅動通過 vring 直接通訊,這就繞過了經過 KVM 核心模組的過程,達到提高 I/O 效能的目的。

 

純軟體模擬的裝置和 Virtio 裝置的區別:virtio 省去了純模擬模式下的異常捕獲環節,Guest OS 可以和 QEMU 的 I/O 模組直接通訊。

 

使用 Virtio 的完整虛機 I/O流程:

Host 資料發到 Guest:

1. KVM 通過中斷的方式通知 QEMU 去獲取資料,放到 virtio queue 中

2. KVM 再通知 Guest 去 virtio queue 中取資料。

2.2 Virtio 在 Linux 中的實現

 Virtio 是在半虛擬化管理程式中的一組通用模擬裝置的抽象。這種設計允許管理程式通過一個應用程式設計介面 (API)對外提供一組通用模擬裝置。通過使用半虛擬化管理程式,客戶機實現一套通用的介面,來配合後面的一套後端裝置模擬。後端驅動不必是通用的,只要它們實現了前端所需的行為。因此,Virtio 是一個在 Hypervisor 之上的抽象API介面,讓客戶機知道自己執行在虛擬化環境中,進而根據 virtio 標準與 Hypervisor 協作,從而客戶機達到更好的效能。

  • 前端驅動:客戶機中安裝的驅動程式模組
  • 後端驅動:在 QEMU 中實現,呼叫主機上的物理裝置,或者完全由軟體實現。
  • virtio 層:虛擬佇列介面,從概念上連線前端驅動和後端驅動。驅動可以根據需要使用不同數目的佇列。比如 virtio-net 使用兩個佇列,virtio-block只使用一個佇列。該佇列是虛擬的,實際上是使用 virtio-ring 來實現的。
  • virtio-ring:實現虛擬佇列的環形緩衝區

Linux 核心中實現的五個前端驅動程式:

  • 塊裝置(如磁碟)
  • 網路裝置
  • PCI 裝置
  • 氣球驅動程式(動態管理客戶機記憶體使用情況)
  • 控制檯驅動程式

Guest OS 中,在不使用 virtio 裝置的時候,這些驅動不會被載入。只有在使用某個 virtio 裝置的時候,對應的驅動才會被載入。每個前端驅動器具有在管理程式中的相應的後端的驅動程式。

以 virtio-net 為例,解釋其原理:

(1)virtio-net 的原理:

 

它使得:

  1. 多個虛機共享主機網絡卡 eth0
  2. QEMU 使用標準的 tun/tap 將虛機的網路橋接到主機網絡卡上
  3. 每個虛機看起來有一個直接連線到主機PCI總線上的私有 virtio 網路裝置
  4. 需要在虛機裡面安裝 virtio驅動

(2)virtio-net 的流程:

總結 Virtio 的優缺點:

  • 優點:更高的IO效能,幾乎可以和原生系統差不多。
  • 缺點:客戶機必須安裝特定的 virtio 驅動。一些老的 Linux 還沒有驅動支援,一些 Windows 需要安裝特定的驅動。不過,較新的和主流的OS都有驅動可以下載了。Linux 2.6.24+ 都預設支援 virtio。可以使用 lsmod | grep virtio 檢視是否已經載入。

2.3 使用 virtio 裝置 (以 virtio-net 為例)

使用 virtio 型別的裝置比較簡單。較新的 Linux 版本上都已經安裝好了 virtio 驅動,而 Windows 的驅動需要自己下載安裝。

(1)檢查主機上是否支援 virtio 型別的網絡卡裝置

[[email protected] isoimages]# kvm -net nic,model=? qemu: Supported NIC models: ne2k_pci,i82551,i82557b,i82559er,rtl8139,e1000,pcnet,virtio

(2)指定網絡卡裝置model 為 virtio,啟動虛機

 

(3)通過 vncviewer 登入虛機,能看到被載入了的 virtio-net 需要的核心模組

(4)檢視 pci 裝置

其它 virtio 型別的裝置的使用方式類似 virtio-net。

2.4 vhost-net (kernel-level virtio server)

  前面提到 virtio 在宿主機中的後端處理程式(backend)一般是由使用者空間的QEMU提供的,然而如果對於網路 I/O 請求的後端處理能夠在在核心空間來完成,則效率會更高,會提高網路吞吐量和減少網路延遲。在比較新的核心中有一個叫做 “vhost-net” 的驅動模組,它是作為一個核心級別的後端處理程式,將virtio-net的後端處理任務放到核心空間中執行,減少核心空間到使用者空間的切換,從而提高效率。

根據 KVM 官網的這篇文章,vhost-net 能提供更低的延遲(latency)(比 e1000 虛擬網絡卡低 10%),和更高的吞吐量(throughput)(8倍於普通 virtio,大概  7~8 Gigabits/sec )。

vhost-net 與 virtio-net 的比較:

 

vhost-net 的要求:

  • qemu-kvm-0.13.0 或者以上
  • 主機核心中設定 CONFIG_VHOST_NET=y 和在虛機作業系統核心中設定 CONFIG_PCI_MSI=y (Red Hat Enterprise Linux 6.1 開始支援該特性)
  • 在客戶機內使用 virtion-net 前段驅動
  • 在主機內使用網橋模式,並且啟動 vhost_net

qemu-kvm 命令的 -net tap 有幾個選項和 vhost-net 相關的: -net tap,[,vnet_hdr=on|off][,vhost=on|off][,vhostfd=h][,vhostforce=on|off]

  • vnet_hdr =on|off:設定是否開啟TAP裝置的“IFF_VNET_HDR”標識。“vnet_hdr=off”表示關閉這個標識;“vnet_hdr=on”則強制開啟這個標識,如果沒有這個標識的支援,則會觸發錯誤。IFF_VNET_HDR是tun/tap的一個標識,開啟它則允許傳送或接受大資料包時僅僅做部分的校驗和檢查。開啟這個標識,可以提高virtio_net驅動的吞吐量。
  • vhost=on|off:設定是否開啟vhost-net這個核心空間的後端處理驅動,它只對使用MIS-X中斷方式的virtio客戶機有效。
  • vhostforce=on|off:設定是否強制使用 vhost 作為非MSI-X中斷方式的Virtio客戶機的後端處理程式。
  • vhostfs=h:設定為去連線一個已經開啟的vhost網路裝置。

vhost-net 的使用例項:

(1)確保主機上 vhost-net 核心模組被載入了

(2)啟動一個虛擬機器,在客戶機中使用 -net 定義一個 virtio-net 網絡卡,在主機端使用 -netdev 啟動 vhost

(3)在虛擬機器端,看到 virtio 網絡卡使用的 TAP 裝置為 tap0。

(4)在宿主機中看 vhost-net 被載入和使用了,以及 Linux 橋 br0,它連線物理網絡卡 eth1 和 客戶機使用的 TAP 裝置 tap0

一般來說,使用 vhost-net 作為後端處理驅動可以提高網路的效能。不過,對於一些網路負載型別使用 vhost-net 作為後端,卻可能使其效能不升反降。特別是從宿主機到其中的客戶機之間的UDP流量,如果客戶機處理接受資料的速度比宿主機發送的速度要慢,這時就容易出現效能下降。在這種情況下,使用vhost-net將會是UDP socket的接受緩衝區更快地溢位,從而導致更多的資料包丟失。故這種情況下,不使用vhost-net,讓傳輸速度稍微慢一點,反而會提高整體的效能。

使用 qemu-kvm 命令列,加上“vhost=off”(或沒有vhost選項)就會不使用vhost-net,而在使用libvirt時,需要對客戶機的配置的XML檔案中的網路配置部分進行如下的配置,指定後端驅動的名稱為“qemu”(而不是“vhost”)。

2.6 virtio-balloon

另一個比較特殊的 virtio 裝置是 virtio-balloon。通常來說,要改變客戶機所佔用的宿主機記憶體,要先關閉客戶機,修改啟動時的記憶體配置,然後重啟客戶機才可以實現。而 記憶體的 ballooning (氣球)技術可以在客戶機執行時動態地調整它所佔用的宿主機記憶體資源,而不需要關閉客戶機。該技術能夠:

  • 當宿主機記憶體緊張時,可以請求客戶機回收利用已分配給客戶機的部分記憶體,客戶機就會釋放部分空閒記憶體。若其記憶體空間不足,可能還會回收部分使用中的記憶體,可能會將部分記憶體換到交換分割槽中。
  • 當客戶機記憶體不足時,也可以讓客戶機的記憶體氣球壓縮,釋放出記憶體氣球中的部分記憶體,讓客戶機使用更多的記憶體。

 

目前很多的VMM,包括 KVM, Xen,VMware 等都對 ballooning 技術提供支援。其中,KVM 中的 Ballooning 是通過宿主機和客戶機協同來實現的,在宿主機中應該使用 2.6.27 及以上版本的 Linux核心(包括KVM模組),使用較新的 qemu-kvm(如0.13版本以上),在客戶機中也使用 2.6.27 及以上核心且將“CONFIG_VIRTIO_BALLOON”配置為模組或編譯到核心。在很多Linux發行版中都已經配置有“CONFIG_VIRTIO_BALLOON=m”,所以用較新的Linux作為客戶機系統,一般不需要額外配置virtio_balloon驅動,使用預設核心配置即可。

原理:

  1. KVM 傳送請求給 VM 讓其歸還一定數量的記憶體給KVM。
  2. VM 的 virtio_balloon 驅動接到該請求。
  3. VM 的驅動是客戶機的記憶體氣球膨脹,氣球中的記憶體就不能被客戶機使用。
  4. VM 的作業系統歸還氣球中的記憶體給VMM
  5. KVM 可以將得到的記憶體分配到任何需要的地方。
  6. KM 也可以將記憶體返還到客戶機中。

優勢和不足:

優勢 不足
  1. ballooning 可以被控制和監控
  2. 對記憶體的調節很靈活,可多可少。
  3. KVM 可以歸還記憶體給客戶機,從而緩解其記憶體壓力。
  1. 需要客戶機安裝驅動
  2. 大量記憶體被回收時,會降低客戶機的效能。
  3. 目前沒有方便的自動化的機制來管理 ballooning,一般都在 QEMU 的 monitor 中執行命令來實現。
  4. 記憶體的動態增加或者減少,可能是記憶體被過度碎片化,從而降低記憶體使用效能。

在QEMU monitor中,提供了兩個命令檢視和設定客戶機記憶體的大小。

  • (qemu) info balloon    #檢視客戶機記憶體佔用量(Balloon資訊)
  • (qemu) balloon num   #設定客戶機記憶體佔用量為numMB

使用例項:

 (1)啟動一個虛機,記憶體為 2048M,啟用 virtio-balloon

(2)通過 vncviewer 進入虛機,檢視 pci 裝置

(3)看看記憶體情況,共 2G 記憶體

(4)進入 QEMU Monitor,調整 balloon 記憶體為 500M

(5)回到虛機,檢視記憶體,變為 500 M

2.7 RedHat 的 多佇列 Virtio (multi-queue)

    目前的高階伺服器都有多個處理器,虛擬使用的虛擬CPU數目也不斷增加。預設的 virtio-net 不能並行地傳送或者接收網路包,因為 virtio_net 只有一個TX 和 RX 佇列。而多佇列 virtio-net 提供了一個隨著虛機的虛擬CPU增加而增強網路效能的方法,通過使得 virtio 可以同時使用多個 virt-queue 佇列。

它在以下情況下具有明顯優勢:

  1. 網路流量非常大
  2. 虛機同時有非常多的網路連線,包括虛擬機器之間的、虛機到主機的、虛機到外部系統的等
  3. virtio 佇列的數目和虛機的虛擬CPU數目相同。這是因為多佇列能夠使得一個佇列獨佔一個虛擬CPU。

注意:對佇列 virtio-net 對流入的網路流工作得非常好,但是對外發的資料流偶爾會降低效能。開啟對佇列 virtio 會增加中的吞吐量,這相應地會增加CPU的負擔。 在實際的生產環境中需要做必須的測試後才確定是否使用。

在 RedHat 中,要使用多佇列 virtio-net,在虛機的 XML 檔案中增加如下配置:

然後在主機上執行下面的命令:

ethtool -L eth0 combined M ( 1 <= M <= N)

2.8 Windows 客戶機的 virtio 前端驅動

Windows 客戶機下的 virtio 前端驅動必須下載後手工安裝。 RedHat Linux 這篇文章 說明了在 Windows 客戶機內安裝virtio 驅動的方法。

參考文件: