- 操作系統(內核)需要直接訪問硬件和內存,因此它的代碼需要運行在最高運行級別 Ring0上,這樣它可以使用特權指令,控制中斷、修改頁表、訪問設備等等。
- 應用程序的代碼運行在最低運行級別上ring3上,不能做受控操作。如果要做,比如要訪問磁盤,寫文件,那就要通過執行系統調用(函數),執行系統調用的時候,CPU的運行級別會發生從ring3到ring0的切換,並跳轉到系統調用對應的內核代碼位置執行,這樣內核就為你完成了設備訪問,完成之后再從ring0返回ring3。這個過程也稱作用戶態和內核態的切換。

1.1 基於二進制翻譯的全虛擬化(Full Virtualization with Binary Translation)




1.2. 超虛擬化(或者半虛擬化/操作系統輔助虛擬化 Paravirtualization)


1.3. 硬件輔助的全虛擬化


(1)一個 KVM 虛機即一個Linux qemu-kvm 進程,與其他 Linux 進程一樣被Linux 進程調度器調度。
(2)KVM 虛機包括虛擬內存、虛擬CPU和虛機 I/O設備,其中,內存和 CPU 的虛擬化由 KVM 內核模塊負責實現,I/O 設備的虛擬化由 QEMU 負責實現。
(3)KVM戶機系統的內存是 qumu-kvm 進程的地址空間的一部分。
(4)KVM 虛機的 vCPU 作為 線程運行在 qemu-kvm 進程的上下文中。
支持虛擬化的 CPU 中都增加了新的功能。以 Intel VT 技術為例,它增加了兩種運行模式:VMX root 模式和 VMX nonroot 模式。通常來講,主機操作系統和 VMM 運行在 VMX root 模式中,客戶機操作系統及其應用運行在 VMX nonroot 模式中。因為兩個模式都支持所有的 ring,因此,客戶機可以運行在它所需要的 ring 中(OS 運行在 ring 0 中,應用運行在 ring 3 中),VMM 也運行在其需要的 ring 中 (對 KVM 來說,QEMU 運行在 ring 3,KVM 運行在 ring 0)。CPU 在兩種模式之間的切換稱為 VMX 切換。從 root mode 進入 nonroot mode,稱為 VM entry;從 nonroot mode 進入 root mode,稱為 VM exit。可見,CPU 受控制地在兩種模式之間切換,輪流執行 VMM 代碼和 Guest OS 代碼。
對 KVM 虛機來說,運行在 VMX Root Mode 下的 VMM 在需要執行 Guest OS 指令時執行 VMLAUNCH 指令將 CPU 轉換到 VMX non-root mode,開始執行客戶機代碼,即 VM entry 過程;在 Guest OS 需要退出該 mode 時,CPU 自動切換到 VMX Root mode,即 VM exit 過程。可見,KVM 客戶機代碼是受 VMM 控制直接運行在物理 CPU 上的。QEMU 只是通過 KVM 控制虛機的代碼被 CPU 執行,但是它們本身並不執行其代碼。也就是說,CPU 並沒有真正的被虛級化成虛擬的 CPU 給客戶機使用。
主機 Linux 將一個虛擬視作一個 QEMU 進程,該進程包括下面幾種線程:
- I/O 線程用於管理模擬設備
- vCPU 線程用於運行 Guest 代碼
- 其它線程,比如處理 event loop,offloaded tasks 等的線程
在我的測試環境中(RedHata Linux 作 Hypervisor):
smp 設置的值 | 線程數 | 線程 |
4 | 8 | 1 個主線程(I/O 線程)、4 個 vCPU 線程、3 個其它線程 |
6 | 10 | 1 個主線程(I/O 線程)、6 個 vCPU 線程、3 個其它線程 |
要將客戶機內的線程調度到某個物理 CPU,需要經歷兩個過程:
- 客戶機線程調度到客戶機物理CPU 即 KVM vCPU,該調度由客戶機操作系統負責,每個客戶機操作系統的實現方式不同。在 KVM 上,vCPU 在客戶機系統看起來就像是物理 CPU,因此其調度方法也沒有什么不同。
- vCPU 線程調度到物理 CPU 即主機物理 CPU,該調度由 Hypervisor 即 Linux 負責。
KVM 使用標准的 Linux 進程調度方法來調度 vCPU 進程。Linux 系統中,線程和進程的區別是 進程有獨立的內核空間,線程是代碼的執行單位,也就是調度的基本單位。Linux 中,線程是就是輕量級的進程,也就是共享了部分資源(地址空間、文件句柄、信號量等等)的進程,所以線程也按照進程的調度方式來進行調度。
我們來假設一個主機有 2 個socket,每個 socket 有 4 個core。主頻2.4G MHZ 那么一共可用的資源是 2*4*2.4G= 19.2G MHZ。假設主機上運行了三個VM,VM1和VM2設置為1socket*1core,VM3設置為1socket*2core。那么VM1和VM2分別有1個vCPU,而VM3有2個vCPU。假設其他設置為缺省設置。
那么三個VM獲得該主機CPU資源分配如下:VM1:25%; VM2:25%; VM3:50%
假設運行在VM3上的應用支持多線程,那么該應用可以充分利用到所非配的CPU資源。2vCPU的設置是合適的。假設運行在VM3上的應用不支持多線程,該應用根本無法同時使用利用2個vCPU. 與此同時,VMkernal層的CPU Scheduler必須等待物理層中兩個空閑的pCPU,才開始資源調配來滿足2個vCPU的需要。在僅有2vCPU的情況下,對該VM的性能不會有太大負面影響。但如果分配4vCPU或者更多,這種資源調度上的負擔有可能會對該VM上運行的應用有很大負面影響。
確定 vCPU 數目的步驟。假如我們要創建一個VM,以下幾步可以幫助確定合適的vCPU數目
1 了解應用並設置初始值
該應用是否是關鍵應用,是否有Service Level Agreement。一定要對運行在虛擬機上的應用是否支持多線程深入了解。咨詢應用的提供商是否支持多線程和SMP(Symmetricmulti-processing)。參考該應用在物理服務器上運行時所需要的CPU個數。如果沒有參照信息,可設置1vCPU作為初始值,然后密切觀測資源使用情況。
2 觀測資源使用情況
確定一個時間段,觀測該虛擬機的資源使用情況。時間段取決於應用的特點和要求,可以是數天,甚至數周。不僅觀測該VM的CPU使用率,而且觀測在操作系統內該應用對CPU的占用率。特別要區分CPU使用率平均值和CPU使用率峰值。
假如分配有4個vCPU,如果在該VM上的應用的CPU
- 使用峰值等於25%, 也就是僅僅能最多使用25%的全部CPU資源,說明該應用是單線程的,僅能夠使用一個vCPU (4 * 25% = 1 )
- 平均值小於38%,而峰值小於45%,考慮減少 vCPU 數目
- 平均值大於75%,而峰值大於90%,考慮增加 vCPU 數目
3 更改vCPU數目並觀測結果
每次的改動盡量少,如果可能需要4vCPU,先設置2vCPU在觀測性能是否可以接受。

- AMD 平台上的 NPT (Nested Page Tables) 技術
- Intel 平台上的 EPT (Extended Page Tables)技術
EPT 和 NPT采用類似的原理,都是作為 CPU 中新的一層,用來將客戶機的物理地址翻譯為主機的物理地址。
(1)初始狀態:
(2)合並后:
(3)Guest 1 寫內存后:
Intel 的 x86 CPU 通常使用4Kb內存頁,當是經過配置,也能夠使用巨頁(huge page): (4MB on x86_32, 2MB on x86_64 and x86_32 PAE)
使用巨頁,KVM的虛擬機的頁表將使用更少的內存,並且將提高CPU的效率。最高情況下,可以提高20%的效率!
大頁面和透明大頁面(THP)
過程 7.1. 為客機啟用 1GB 大頁面
-
Red Hat Enterprise Linux 7.1 系統支持 2MB 或 1GB 大頁面,分配將在啟動或運行時進行。頁面大小均可以在運行時被釋放。例如,在啟動時分配 4 個 1GB 的大頁面和 1,024 個 2MB 的大頁面,請使用以下命令行:
'default_hugepagesz=1G hugepagesz=1G hugepages=4 hugepagesz=2M hugepages=1024'
此外,大頁面還可以在運行時分配。運行時分配允許系統管理員選擇從何種 NUMA 模式分配頁面。然而由於內存碎片的存在,運行時的頁面分配會比啟動時分配更容易造成分配失敗。以下運行時的分配示例顯示了從node1
分配 4 個 1GB 的大頁面以及從node3
分配 1,024 個 2MB 的大頁面:# echo 4 > /sys/devices/system/node/node1/hugepages/hugepages-1048576kB/nr_hugepages # echo 1024 > /sys/devices/system/node/node3/hugepages/hugepages-2048kB/nr_hugepages
-
接下來,將 2MB 和 1GB 的大頁面掛載到主機:
# mkdir /dev/hugepages1G # mount -t hugetlbfs -o pagesize=1G none /dev/hugepages1G # mkdir /dev/hugepages2M # mount -t hugetlbfs -o pagesize=2M none /dev/hugepages2M
<memoryBacking> <hugepages/> <page size="1" unit="G" nodeset="0-3,5"/> <page size="2" unit="M" nodeset="4"/> </hugepages> </memoryBacking>
/sys/kernel/mm/transparent_hugepage/enabled
被設置為
always
,透明大頁面將被默認使用。運行以下命令禁用透明大頁面:
# echo never > /sys/kernel/mm/transparent_hugepage/enabled
例子:
使用方法,需要三部:
mkdir /dev/hugepages
mount -t hugetlbfs hugetlbfs /dev/hugepages
#保留一些內存給巨頁 sysctl vm.nr_hugepages=2048 (使用 x86_64 系統時,這相當於從物理內存中保留了2048 x 2M = 4GB 的空間來給虛擬機使用)
#給 kvm 傳遞參數 hugepages qemu-kvm - qemu-kvm -mem-path /dev/hugepages
也可以在配置文件里加入:
<memoryBacking> <hugepages/> </memoryBacking>
驗證方式,當虛擬機正常啟動以后,在物理機里查看:
cat /proc/meminfo |grep -i hugepages