KVM 核心功能:內存虛擬化



1 內存虛擬化簡介

QEMU-KVM 提供內存的虛擬化,從虛擬機角度看其自身擁有的內存就是真實的物理內存。實際上,虛擬機是 host 上的一個 qemu 進程,在為虛擬機指定內存時,host 上並沒有分配該內存給虛擬機(qemu 進程),而是需要使用內存時,由 qemu-kvm 分配內存給它。
 
這里了解 QEMU-KVM 內存虛擬化機制。

2 內存虛擬化配置

傳統的內存虛擬化通過影子頁表實現,但是影子頁表實現復雜,而且內存開銷很大,每個 qemu 進程都需要維護自己的影子頁表。
基於硬件支持的內存虛擬化技術能較好的解決影子頁表帶來的問題,Intel CPU 提供 EPT 技術支持內存虛擬化,AMD 提供 NPT 技術支持內存虛擬化。
 
在 host 上查看硬件是否支持內存虛擬化:
[demo@lianhua ~]$ cat /proc/cpuinfo | grep "model name"
model name      : Intel(R) Xeon(R) CPU E5-2630 v4 @ 2.20GHz
 
[demo@lianhua ~]$ cat /proc/cpuinfo | grep -E "ept|vpid"
fpu_exception   : yes
flags           : ...  vmx smx aes tpr_shadow vnmi flexpriority ept vpid fsgsbase 
可以看出,host 上使用的是 Intel 的 CPU,該 CPU 已經支持了 ept 和 vpid。
 
kvm_intel 模塊在加載時會默認開啟 ept 和 vpid,從而 KVM 可以使用它們:
[demo@lianhua ~]$ cat /sys/module/kvm_intel/parameters/ept
Y
[demo@lianhua ~]$ cat /sys/module/kvm_intel/parameters/vpid
Y
 
在創建虛擬機時,通過 qemu-kvm 的 -m 選項指定虛擬機的內存:
[root@lianhua ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           251G         85G         11G         56M        155G        159G
Swap:            0B          0B          0B
 
[root@lianhua ~]#  /usr/libexec/qemu-kvm -m 5G -smp 2 demo.qcow2 -monitor stdio
QEMU 2.6.0 monitor - type 'help' for more information
(qemu) VNC server running on '::1;5900'
(qemu)
 
[root@lianhua ~]# free -h
              total        used        free      shared  buff/cache   available
Mem:           251G         85G         11G         56M        155G        159G
Swap:            0B          0B          0B
 
如上所示,指定虛擬機的內存為 5G,在創建虛擬機之后, host 上並沒有分配 5G 內存給虛擬機。

3 內存虛擬化特性

類似 vCPU 虛擬化特性,內存虛擬化也有幾大特性:

3.1 內存過載(over-commit)

顧名思義,內存過載即 host 上總的虛擬機內存可以大於 host 內存。在 host 上不同虛擬機可能實現不同的功能,不同功能使用的內存不盡相同,一般不會保證內存 100% 被使用到。因此,可以對內存進行過載分配。
 
內存過載分配主要有三種方式:
  • 內存交換(swapping):通過 host 上的 swap 交換分區分配過載的內存。
  • 內存氣球(ballooning):通過半虛擬化的 ballooning 機制實現內存過載分配。
  • 頁共享(page sharing):通過 KSM(Kernel Samepage Merging) 合並多個相同內存頁實現內存過載分配。

3.1.1 內存交換

內存交換是最常用的內存過載分配方式。它是在虛擬機內存需要過載分配時,將 swap 交換分區的內存分配給虛擬機,從而實現內存的過載分配。
 
使用內存交換進行內存過載分配時,用戶不需要顯示的指定其它配置,但是要保證 swap 交換分區有足夠多的空間可供分配。比如,host 上有 10 台虛擬機,每台虛擬機指定的內存為 1G,而 host 上的物理內存有 6G,那么,不算操作系統自己占用內存的情況下,還需要至少 4G 的內存才能實現虛擬機內存的過載,這至少 4G 的內存就要從 swap 交換分區中分配。
 
注意:swap 交換分區是使用磁盤存儲內存數據的分區,相比於直接使用內存條存儲數據要慢的多。如果虛擬機對讀寫性能有要求的話,那么,在使用內存交換進行內存過載分配之前還需要評估性能是否受影響。

3.1.2 內存氣球

3.1.2.1 內存氣球簡介

內存氣球是通過半虛擬化機制的 ballooning 方式實現的內存過載分配。如下圖所示:
 
內存氣球是在虛擬機內部引入氣球的概念,氣球內的內存不能被虛擬機使用。當 hypervisor 請求擴大內存時,虛擬機內部的氣球就會膨脹,內存頁就會釋放。當虛擬機需要擴大內存時,虛擬機內部的氣球就會縮小,可用的內存增多。引入內存氣球的機制可以動態調整虛擬機的內存。
 
ballooning 方式是在半虛擬化機制下工作的(半虛擬化原理可看這里),在 host 上配置的 virtio 后端驅動是 virtio_balloon,在虛擬機中配置的 virtio 前端驅動是 virtio-pci:
[root@lianhua ~]# find /lib/modules/3.10.0-514.6.2.el7.x86_64/ -name virtio*.ko
...
/lib/modules/3.10.0-514.6.2.el7.x86_64/kernel/drivers/virtio/virtio_balloon.ko
 
[root@vm:/sys/module]
# ls /sys/module/virtio*
/sys/module/virtio_blk:
parameters  uevent
 
/sys/module/virtio_net:
parameters  uevent
 
/sys/module/virtio_pci:
drivers  parameters  uevent  version
(驅動加載位置因操作系統及配置而異,並不是所有機器的驅動模塊都在這個路徑下)
 
結合 virtio 的驅動及內存氣球工作原理,再次介紹內存氣球的工作流程:
1) hypervisor 發送請求給虛擬機,請求虛擬機釋放內存。
2) 虛擬機的 virtio-pci 驅動接收到該請求,並且使虛擬機內部的內存氣球膨脹。
3) 內存氣球膨脹之后,虛擬機可用的內存減少,虛擬機通知 hypervisor 內存已釋放。
4) hypervisor 將釋放的內存分配到需要內存的地方。

3.1.2.2 內存氣球配置

配置內存氣球,首先需要在 host 上加載 virtio_ballon 模塊。然后,通過 qemu-kvm 的 -device 選項指定虛擬機的內存驅動為 virtio-balloon-pci:
[root@lianhua hxia]# /usr/libexec/qemu-kvm -enable-kvm -smp 2 -m 2G -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x9 -msg timestamp=on  demo.qcow2 -monitor stdio
QEMU 2.6.0 monitor - type 'help' for more information
(qemu) VNC server running on '::1;5900'
 
如上所示,指定虛擬機的內存為 2G,且內存分配方式為 virtio ballooning 分配,balloon 設備的 pci 號為 00:09:0。
 
進入創建的虛擬機,查看 pci 編號及 balloon 設備所使用的驅動:
[root@vm:/home/robot]
# lspci
...
00:09.0 Unclassified device [00ff]: Red Hat, Inc. Virtio memory balloon   
 
[root@vm:/sys/module]
# lspci -s 00:09.0 -vvv
00:09.0 Unclassified device [00ff]: Red Hat, Inc. Virtio memory balloon
        Subsystem: Red Hat, Inc. Device 0005
        Physical Slot: 9
        Control: I/O+ Mem+ BusMaster+ SpecCycle- MemWINV- VGASnoop- ParErr- Stepping- SERR+ FastB2B- DisINTx-
        Status: Cap+ 66MHz- UDF- FastB2B- ParErr- DEVSEL=fast >TAbort- <TAbort- <MAbort- >SERR- <PERR- INTx-
        Latency: 0
        Interrupt: pin A routed to IRQ 10
        Region 0: I/O ports at c0c0 [size=32]
        Region 4: Memory at fe010000 (64-bit, prefetchable) [size=16K]
        Capabilities: [84] Vendor Specific Information: VirtIO: <unknown>
                BAR=0 offset=00000000 size=00000000
        Capabilities: [70] Vendor Specific Information: VirtIO: Notify
                BAR=4 offset=00003000 size=00001000 multiplier=00000004
        Capabilities: [60] Vendor Specific Information: VirtIO: DeviceCfg
                BAR=4 offset=00002000 size=00001000
        Capabilities: [50] Vendor Specific Information: VirtIO: ISR
                BAR=4 offset=00001000 size=00001000
        Capabilities: [40] Vendor Specific Information: VirtIO: CommonCfg
                BAR=4 offset=00000000 size=00001000
        Kernel driver in use: virtio-pci        # balloon 所用的驅動為 virtio-pci 
 
除了 qemu-kvm 指定 balloon 設備創建虛擬機的方式外,還可以在 libvirt 的 XML 文件中指定 balloon 設備來創建虛擬機:
<memballoon model='virtio'>
  <stats period='10'/>
  <address type='pci' domain='0x0000' bus='0x00' slot='0x09' function='0x0'/>
</memballoon>

3.1.2.3 內存氣球測試

內存氣球配置好以后,進入 qemu 的 monitor 通過執行 balloon 命令手動分配虛擬機的內存(注意 balloon 機制都是通過調用命令行的方式調整虛擬機的內存的,不是內核自發完成的):
[root@lianhua hxia]# /usr/libexec/qemu-kvm -enable-kvm -smp 2 -m 512 -device virtio-balloon-pci,id=balloon0,bus=pci.0,addr=0x9 -msg timestamp=on  demo.qcow2 -monitor stdio
QEMU 2.6.0 monitor - type 'help' for more information
(qemu) VNC server running on '::1;5900'
 
(qemu) info balloon
balloon: actual=512
(qemu) balloon 256
(qemu) info balloon
balloon: actual=256
(qemu) balloon 512
(qemu) info balloon
balloon: actual=512
(qemu) balloon 666
(qemu) info balloon
balloon: actual=512
 
如上所示,在 monitor 中通過調用 balloon 命令可以動態調節虛擬機內存大小,在虛擬機中查看內存確實隨之而變化。另一方面可以看出,當試圖調整 balloon 到超過內存上限 512M 時,實際的內存大小還是 512M,balloon 並沒有變化。

3.1.3 頁共享

3.1.3.1 頁共享簡介

頁共享是基於 KSM 實現的內存過載分配方式。KSM(Kernel Samepage Merging)內核同頁合並會合並內存中的相同內存頁,減少內存使用量,從而實現內存過載分配。
應用程序會標記可合並的內存頁,KSM 掃描到這些可合並內存頁,然后對其進行合並,通常合並是沒有風險的。如果應用程序需要修改合並的內存頁,則內核會通過“寫時復制(copy-on-write,cow)”技術復制一份內存頁,然后對復制的內存頁進行改寫,從而保證了原內存頁的安全。
 
RedHat 系列系統默認安裝了 ksm 和 ksmtuned 服務程序,使用 ps 命令查看 host 上是否運行 ksm 和 ksmtuned:
[root@lianhua home]# cat /etc/redhat-release
Red Hat Enterprise Linux Server release 7.3 (Maipo)
 
[root@lianhua home]# ps -elf | grep ksm | grep -v grep
1 S root         306       2  0  85   5 -     0 ksm_sc Apr30 ?        01:47:01 [ksmd]
1 S root        1158       1  0  80   0 - 29694 wait   Apr30 ?        00:09:34 /bin/bash /usr/sbin/ksmtuned
 
可見,確實安裝了 ksm 和 ksmtuned 服務程序,並且程序的進程正在“運行”中。其中,ksm 是主要的合並相同內存頁的服務程序,ksmtuned 是對 ksm 的參數配置進行微調的服務程序,因為 ksm 的配置一旦修改了,系統默認不會再去修改它的值,這樣對於掃描,合並內存頁不夠靈活。而 ksmtuned 可以實時動態調節 ksm 的行為。
 
ksmtuned 的配置參數有:
[root@lianhua home]# cat /etc/ksmtuned.conf
# Configuration file for ksmtuned.
 
# How long ksmtuned should sleep between tuning adjustments
# KSM_MONITOR_INTERVAL=60
 
# Millisecond sleep between ksm scans for 16Gb server.
# Smaller servers sleep more, bigger sleep less.
# KSM_SLEEP_MSEC=10
 
# KSM_NPAGES_BOOST=300
# KSM_NPAGES_DECAY=-50
# KSM_NPAGES_MIN=64
# KSM_NPAGES_MAX=1250
 
# KSM_THRES_COEF=20
# KSM_THRES_CONST=2048
 
# uncomment the following if you want ksmtuned debug info
 
# LOGFILE=/var/log/ksmtuned
# DEBUG=1
(看這里了解 ksmtuned 配置參數)

3.1.2.2 頁共享配置

使用 qemu-kvm 創建虛擬機時,可指定 -machine 選項的 mem-merge 開關控制內存頁共享,mem-merge 為 on 即表示打開內存頁共享,off 即表示關閉內存頁共享:
[root@lianhua qemu-kvm]# /usr/libexec/qemu-kvm -m 500M -smp 2 lianhua.raw --machine mem-merge=on -monitor stdio
WARNING: Image format was not specified for 'lianhua.raw' and probing guessed raw.
         Automatically detecting the format is dangerous for raw images, write operations on block 0 will be restricted.
         Specify the 'raw' format explicitly to remove the restrictions.
QEMU 2.6.0 monitor - type 'help' for more information
(qemu) VNC server running on '::1;5900'
 
(qemu)
 
在 /sys/kernel/mm/ksm/ 目錄下查看 ksm 合並的內存頁文件:
[root@lianhua home]# ll /sys/kernel/mm/ksm/
total 0
-r--r--r--. 1 root root 4096 Jul 31 13:06 full_scans
-rw-r--r--. 1 root root 4096 Jul 31 13:06 max_page_sharing
-rw-r--r--. 1 root root 4096 Jul 31 13:06 merge_across_nodes
-r--r--r--. 1 root root 4096 Jul 11 17:37 pages_shared
-r--r--r--. 1 root root 4096 Jul 11 17:37 pages_sharing
-rw-r--r--. 1 root root 4096 Jul 12 00:46 pages_to_scan
-r--r--r--. 1 root root 4096 Jul 31 13:06 pages_unshared
-r--r--r--. 1 root root 4096 Jul 31 13:06 pages_volatile
-rw-r--r--. 1 root root 4096 Aug  2 01:05 run
-rw-r--r--. 1 root root 4096 Jul 12 00:46 sleep_millisecs
-r--r--r--. 1 root root 4096 Jul 31 13:06 stable_node_chains
-rw-r--r--. 1 root root 4096 Jul 31 13:06 stable_node_chains_prune_millisecs
-r--r--r--. 1 root root 4096 Jul 31 13:06 stable_node_dups
 
[root@lianhua home]# cat /sys/kernel/mm/ksm/run
0
 
主要文件有:
  • merge_across_nodes:是否允許跨 NUMA 節點合並相同內存頁。
  • pages_shared:標記已經在用的內存頁數量。
  • pages_sharing:標記合並的內存頁數量。
  • pages_to_scan:標記 ksmd 休眠之前掃描的內存頁數量。
  • run:標記 ksm 是否運行,有多個標志位,標志位 0 表示停止運行 ksmd 進程但保存已合並的內存頁;1 表示運行 ksmd 進程,2 表示停止 ksmd 進程。
 
從上述文件可知,pages_sharing 除以 pages size 即為共享的內存頁大小。
 
在 host 上創建 2 個虛擬機,且都打開內存頁共享,查看 host 上共享的內存頁大小:
[root@lianhua home]# echo "$(( $(cat /sys/kernel/mm/ksm/pages_sharing) * $(getconf PAGESIZE) / 1024 / 1024))MB"
5MB

3.2 內存熱插拔

內存虛擬化的另一個特性是內存熱插拔,看這里了解內存熱插拔特性。
 
 
 


免責聲明!

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



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