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 內存熱插拔

內存虛擬化的另外一個特性是內存熱插拔,看這裏瞭解內存熱插拔特性。
相關文章
相關標籤/搜索