1 內存虛擬化簡介
QEMU-KVM 提供內存的虛擬化,從虛擬機角度看其自身擁有的內存就是真實的物理內存。實際上,虛擬機是 host 上的一個 qemu 進程,在爲虛擬機指定內存時,host 上並無分配該內存給虛擬機(qemu 進程),而是須要使用內存時,由 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
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
主要文件有:
-
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 內存熱插拔
內存虛擬化的另外一個特性是內存熱插拔,看這裏瞭解內存熱插拔特性。