簡(jiǎn)介: Linuxlguest
)而增加。但是,讓這些虛擬化模式能夠在 Linux 之上運(yùn)行之后,又如何讓它們能夠在 I/O 虛擬化方面利用底層內(nèi)核呢?答案是使用 virtio
,它為 hypervisor 和一組通用的 I/O 虛擬化驅(qū)動(dòng)程序提供高效的抽象。探索 virtio 并了解為什么 Linux 將成為最佳的 hypervisor。
分享您的技能: 支持特定的 I/O 虛擬化模式影響您選擇使用指定的 hypervisor 嗎?請(qǐng)?jiān)谙旅?添加您的評(píng)論。
概而言之,virtio
是半虛擬化 hypervisor 中位于設(shè)備之上的抽象層。virtio
由 Rusty Russell 開(kāi)發(fā),他當(dāng)時(shí)的目的是支持自己的虛擬化解決方案 lguest
。本文在開(kāi)篇時(shí)介紹半虛擬化和模擬設(shè)備,然后探索 virtio
的細(xì)節(jié)。本文的重點(diǎn)是來(lái)自 2.6.30 內(nèi)核發(fā)行版的 virtio
框架。
Linux 是 hypervisor 展臺(tái)。如我的 剖析 Linux hypervisor 所述,Linux 提供各種 hypervisor 解決方案,這些解決方案都有自己的特點(diǎn)和優(yōu)點(diǎn)。這些解決方案包括 Kernel-based Virtual Machine (KVM)、lguest
和 User-mode Linux 等。在 Linux 上配備這些不同的 hypervisor 解決方案會(huì)給操作系統(tǒng)帶來(lái)負(fù)擔(dān),負(fù)擔(dān)的大小取決于各個(gè)解決方案的需求。其中的一項(xiàng)開(kāi)銷為設(shè)備的虛擬化。virtio
并沒(méi)有提供多種設(shè)備模擬機(jī)制(針對(duì)網(wǎng)絡(luò)、塊和其他驅(qū)動(dòng)程序),而是為這些設(shè)備模擬提供一個(gè)通用的前端,從而標(biāo)準(zhǔn)化接口和增加代碼的跨平臺(tái)重用。
讓我們快速討論一下兩種類型完全不同的虛擬化模式:完全虛擬化和半虛擬化。在完全虛擬化 中,來(lái)賓操作系統(tǒng)運(yùn)行在位于物理機(jī)器上的 hypervisor 之上。來(lái)賓操作系統(tǒng)并不知道它已被虛擬化,并且不需要任何更改就可以在該配置下工作。相反,在半虛擬化 中,來(lái)賓操作系統(tǒng)不僅知道它運(yùn)行在 hypervisor 之上,還包含讓來(lái)賓操作系統(tǒng)更高效地過(guò)渡到 hypervisor 的代碼(見(jiàn) 圖 1)。
在完全虛擬化模式中,hypervisor 必須模擬設(shè)備硬件,它是在會(huì)話的最低級(jí)別進(jìn)行模擬的(例如,網(wǎng)絡(luò)驅(qū)動(dòng)程序)。盡管在該抽象中模擬很干凈,但它同時(shí)也是最低效、最復(fù)雜的。在半虛擬化模式中,來(lái)賓操作系統(tǒng)和 hypervisor 能夠共同合作,讓模擬更加高效。半虛擬化方法的缺點(diǎn)是操作系統(tǒng)知道它被虛擬化,并且需要修改才能工作。
硬件隨著虛擬化技術(shù)而不斷改變。新的處理器通過(guò)納入高級(jí)指令來(lái)讓來(lái)賓操作系統(tǒng)到 hypervisor 的過(guò)渡更加高效。此外,硬件也隨著輸入/輸出(I/O)虛擬化而不斷改變(參見(jiàn) 參考資料 了解 Peripheral Controller Interconnect [PCI] passthrough 和 single- and multi-root I/O 虛擬化)。
virtio
并不是該領(lǐng)域中的唯一霸主。Xen 提供半虛擬化設(shè)備驅(qū)動(dòng)程序,VMware 也提供 Guest Tools。
但是在傳統(tǒng)的完全虛擬化環(huán)境中,hypervisor 必須捕捉這些請(qǐng)求,然后模擬物理硬件的行為。盡管這樣做提供很大的靈活性(即運(yùn)行未更改的操作系統(tǒng)),但它的效率比較低(參見(jiàn) 圖 1 左邊)。圖 1 的右邊是半虛擬化示例。在這里,來(lái)賓操作系統(tǒng)知道它運(yùn)行在 hypervisor 之上,并包含了充當(dāng)前端的驅(qū)動(dòng)程序。Hypervisor 為特定的設(shè)備模擬實(shí)現(xiàn)后端驅(qū)動(dòng)程序。通過(guò)在這些前端和后端驅(qū)動(dòng)程序中的 virtio
,為開(kāi)發(fā)模擬設(shè)備提供標(biāo)準(zhǔn)化接口,從而增加代碼的跨平臺(tái)重用率并提高效率。
從前面的小節(jié)可以看到,virtio
是對(duì)半虛擬化 hypervisor 中的一組通用模擬設(shè)備的抽象。該設(shè)置還允許 hypervisor 導(dǎo)出一組通用的模擬設(shè)備,并通過(guò)一個(gè)通用的應(yīng)用編程接口(API)讓它們變得可用。圖 2 展示了為什么這很重要。有了半虛擬化 hypervisor 之后,來(lái)賓操作系統(tǒng)能夠?qū)崿F(xiàn)一組通用的接口,在一組后端驅(qū)動(dòng)程序之后采用特定的設(shè)備模擬。后端驅(qū)動(dòng)程序不需要是通用的,因?yàn)樗鼈冎粚?shí)現(xiàn)前端所需的行為。
注意,在現(xiàn)實(shí)中(盡管不需要),設(shè)備模擬發(fā)生在使用 QEMU 的空間,因此后端驅(qū)動(dòng)程序與 hypervisor 的用戶空間交互,以通過(guò) QEMU 為 I/O 提供便利。QEMU 是一個(gè)系統(tǒng)模擬器,它不僅提供來(lái)賓操作系統(tǒng)虛擬化平臺(tái),還提供整個(gè)系統(tǒng)(PCI 主機(jī)控制器、磁盤、網(wǎng)絡(luò)、視頻硬件、USB 控制器和其他硬件元素)的模擬。
virtio
API 依賴一個(gè)簡(jiǎn)單的緩沖抽象來(lái)封裝來(lái)賓操作系統(tǒng)需要的命令和數(shù)據(jù)。讓我們查看 virtio
API 的內(nèi)部及其組件。
除了前端驅(qū)動(dòng)程序(在來(lái)賓操作系統(tǒng)中實(shí)現(xiàn))和后端驅(qū)動(dòng)程序(在 hypervisor 中實(shí)現(xiàn))之外,virtio
還定義了兩個(gè)層來(lái)支持來(lái)賓操作系統(tǒng)到 hypervisor 的通信。在頂級(jí)(稱為 virtio)的是虛擬隊(duì)列接口,它在概念上將前端驅(qū)動(dòng)程序附加到后端驅(qū)動(dòng)程序。驅(qū)動(dòng)程序可以使用 0 個(gè)或多個(gè)隊(duì)列,具體數(shù)量取決于需求。例如,virtio
網(wǎng)絡(luò)驅(qū)動(dòng)程序使用兩個(gè)虛擬隊(duì)列(一個(gè)用于接收,另一個(gè)用于發(fā)送),而 virtio
塊驅(qū)動(dòng)程序僅使用一個(gè)虛擬隊(duì)列。虛擬隊(duì)列實(shí)際上被實(shí)現(xiàn)為跨越來(lái)賓操作系統(tǒng)和 hypervisor 的銜接點(diǎn)。但這可以通過(guò)任意方式實(shí)現(xiàn),前提是來(lái)賓操作系統(tǒng)和 hypervisor 以相同的方式實(shí)現(xiàn)它。
如 圖 3 所示,分別為塊設(shè)備(比如磁盤)、網(wǎng)絡(luò)設(shè)備、PCI 模擬和 balloon 驅(qū)動(dòng)程序列出了 5 個(gè)前端驅(qū)動(dòng)程序。每個(gè)前端驅(qū)動(dòng)程序在 hypervisor 中有一個(gè)對(duì)應(yīng)的后端驅(qū)動(dòng)程序。
從來(lái)賓操作系統(tǒng)的角度來(lái)看,對(duì)象層次結(jié)構(gòu) 的定義如 圖 4 所示。在頂級(jí)的是 virtio_driver
,它在來(lái)賓操作系統(tǒng)中表示前端驅(qū)動(dòng)程序。與該驅(qū)動(dòng)程序匹配的設(shè)備由 virtio_device
(設(shè)備在來(lái)賓操作系統(tǒng)中的表示)封裝。這引用 virtio_config_ops
結(jié)構(gòu)(它定義配置 virtio
設(shè)備的操作)。virtio_device
由 virtqueue
引用(它包含一個(gè)到它服務(wù)的 virtio_device
的引用)。最后,每個(gè) virtqueue
對(duì)象引用 virtqueue_ops
對(duì)象,后者定義處理 hypervisor 的驅(qū)動(dòng)程序的底層隊(duì)列操作。盡管隊(duì)列操作是 virtio
API 的核心,我還是先簡(jiǎn)單討論一下新的發(fā)現(xiàn),然后再詳細(xì)探討 virtqueue_ops
操作。
該流程以創(chuàng)建 virtio_driver
并通過(guò) register_virtio_driver
進(jìn)行注冊(cè)開(kāi)始。virtio_driver
結(jié)構(gòu)定義上層設(shè)備驅(qū)動(dòng)程序、驅(qū)動(dòng)程序支持的設(shè)備 ID 的列表、一個(gè)特性表單(取決于設(shè)備類型)和一個(gè)回調(diào)函數(shù)列表。當(dāng) hypervisor 識(shí)別到與設(shè)備列表中的設(shè)備 ID 相匹配的新設(shè)備時(shí),將調(diào)用 probe
函數(shù)(由 virtio_driver
對(duì)象提供)來(lái)傳入 virtio_device
對(duì)象。將這個(gè)對(duì)象和設(shè)備的管理數(shù)據(jù)緩存起來(lái)(以獨(dú)立于驅(qū)動(dòng)程序的方式緩存)。可能要調(diào)用 virtio_config_ops
函數(shù)來(lái)獲取或設(shè)置特定于設(shè)備的選項(xiàng),例如,為 virtio_blk
設(shè)備獲取磁盤的 Read/Write 狀態(tài)或設(shè)置塊設(shè)備的塊大小,具體情況取決于啟動(dòng)器的類型。
注意,virtio_device
不包含到 virtqueue
的引用(但 virtqueue
確實(shí)引用了 virtio_device
)。要識(shí)別與該 virtio_device
相關(guān)聯(lián)的 virtqueue
,您需要結(jié)合使用 virtio_config_ops
對(duì)象和 find_vq
函數(shù)。該對(duì)象返回與這個(gè) virtio_device
實(shí)例相關(guān)聯(lián)的虛擬隊(duì)列。find_vq
函數(shù)還允許為 virtqueue
指定一個(gè)回調(diào)函數(shù)(查看 圖 4 中的 virtqueue
結(jié)構(gòu))。
virtqueue
是一個(gè)簡(jiǎn)單的結(jié)構(gòu),它識(shí)別一個(gè)可選的回調(diào)函數(shù)(在 hypervisor 使用緩沖池時(shí)調(diào)用)、一個(gè)到 virtio_device
的引用、一個(gè)到 virtqueue
操作的引用,以及一個(gè)引用要使用的底層實(shí)現(xiàn)的特殊 priv
引用。雖然 callback
是可選的,但是它能夠動(dòng)態(tài)地啟用或禁用回調(diào)。
該層次結(jié)構(gòu)的核心是 virtqueue_ops
,它定義在來(lái)賓操作系統(tǒng)和 hypervisor 之間移動(dòng)命令和數(shù)據(jù)的方式。讓我們首先探索添加到或從 virtqueue
移除的對(duì)象。
來(lái)賓操作系統(tǒng)(前端)驅(qū)動(dòng)程序通過(guò)緩沖池與 hypervisor 交互。對(duì)于 I/O,來(lái)賓操作系統(tǒng)提供一個(gè)或多個(gè)表示請(qǐng)求的緩沖池。例如,您可以提供 3 個(gè)緩沖池,第一個(gè)表示 Read 請(qǐng)求,后面兩個(gè)表示響應(yīng)數(shù)據(jù)。該配置在內(nèi)部被表示為一個(gè)散集列表(scatter-gather),列表中的每個(gè)條目表示一個(gè)地址和一個(gè)長(zhǎng)度。
通過(guò) virtio_device
和 virtqueue
(更常見(jiàn))將來(lái)賓操作系統(tǒng)驅(qū)動(dòng)程序與 hypervisor 的驅(qū)動(dòng)程序鏈接起來(lái)。virtqueue
支持它自己的由 5 個(gè)函數(shù)組成的 API。您可以使用第一個(gè)函數(shù) add_buf
來(lái)向 hypervisor 提供請(qǐng)求。如前面所述,該請(qǐng)求以散集列表的形式存在。對(duì)于 add_buf
,來(lái)賓操作系統(tǒng)提供用于將請(qǐng)求添加到隊(duì)列的 virtqueue
、散集列表(地址和長(zhǎng)度數(shù)組)、用作輸出條目(目標(biāo)是底層 hypervisor)的緩沖池?cái)?shù)量,以及用作輸入條目(hypervisor 將為它們儲(chǔ)存數(shù)據(jù)并返回到來(lái)賓操作系統(tǒng))的緩沖池?cái)?shù)量。當(dāng)通過(guò) add_buf
向 hypervisor 發(fā)出請(qǐng)求時(shí),來(lái)賓操作系統(tǒng)能夠通過(guò) kick
函數(shù)通知 hypervisor 新的請(qǐng)求。為了獲得最佳的性能,來(lái)賓操作系統(tǒng)應(yīng)該在通過(guò) kick
發(fā)出通知之前將盡可能多的緩沖池裝載到 virtqueue
。
通過(guò) get_buf
函數(shù)觸發(fā)來(lái)自 hypervisor 的響應(yīng)。來(lái)賓操作系統(tǒng)僅需調(diào)用該函數(shù)或通過(guò)提供的 virtqueue callback
函數(shù)等待通知就可以實(shí)現(xiàn)輪詢。當(dāng)來(lái)賓操作系統(tǒng)知道緩沖區(qū)可用時(shí),調(diào)用 get_buf
返回完成的緩沖區(qū)。
virtqueue
API 的最后兩個(gè)函數(shù)是 enable_cb
和 disable_cb
。您可以使用這兩個(gè)函數(shù)來(lái)啟用或禁用回調(diào)進(jìn)程(通過(guò)在 virtqueue
中由 virtqueue
初始化的 callback
函數(shù))。注意,該回調(diào)函數(shù)和 hypervisor 位于獨(dú)立的地址空間中,因此調(diào)用通過(guò)一個(gè)間接的 hypervisor 來(lái)觸發(fā)(比如 kvm_hypercall
)。
緩沖區(qū)的格式、順序和內(nèi)容僅對(duì)前端和后端驅(qū)動(dòng)程序有意義。內(nèi)部傳輸(當(dāng)前實(shí)現(xiàn)中的連接點(diǎn))僅移動(dòng)緩沖區(qū),并且不知道它們的內(nèi)部表示。
您可以在 Linux 內(nèi)核的 ./drivers 子目錄內(nèi)找到各種前端驅(qū)動(dòng)程序的源代碼??梢栽?./drivers/net/virtio_net.c 中找到 virtio
網(wǎng)絡(luò)驅(qū)動(dòng)程序,在 ./drivers/block/virtio_blk.c 中找到 virtio
塊驅(qū)動(dòng)程序。子目錄 ./drivers/virtio 提供 virtio
接口的實(shí)現(xiàn)(virtio
設(shè)備、驅(qū)動(dòng)程序、virtqueue
和連接點(diǎn))。virtio
還應(yīng)用在 High-Performance Computing (HPC) 研究中,以開(kāi)發(fā)出通過(guò)共享內(nèi)存?zhèn)鬟f的 inter-virtual machine (VM) 通信。尤其是,這是通過(guò)使用 virtio
PCI 驅(qū)動(dòng)程序的虛擬化 PCI 接口實(shí)現(xiàn)的。您可以在 參考資料 部分更多地了解這個(gè)知識(shí)點(diǎn)。
現(xiàn)在,您可以在 Linux 內(nèi)核中實(shí)踐這個(gè)半虛擬化基礎(chǔ)架構(gòu)。您所需的包括一個(gè)充當(dāng) hypervisor 的內(nèi)核、一個(gè)來(lái)賓操作性內(nèi)核和用于設(shè)備模擬的 QEMU。您可以使用 KVM(位于主機(jī)內(nèi)核中的一個(gè)模塊)或 Rusty Russell 的 lguest
(修改版的 Linux 來(lái)賓操作系統(tǒng)內(nèi)核)。這兩個(gè)虛擬化解決方案都支持 virtio
(以及用于系統(tǒng)模擬的 QEMU 和用于虛擬化管理的 libvirt
)。
Rusty 的 lguest
是針對(duì)半虛擬化驅(qū)動(dòng)程序和更快速地模擬虛擬設(shè)備的更簡(jiǎn)潔代碼庫(kù)。但更重要的是,實(shí)踐證明 virtio
比現(xiàn)有的商業(yè)解決方案提供更出色的性能(網(wǎng)絡(luò) I/O 能夠提升 2-3 倍)。性能的提升是需要付出代價(jià)的,但是如果您使用 Linux 作為 hypervisor 和來(lái)賓操作系統(tǒng),那么這樣做是值得的。
也許您可能從來(lái)沒(méi)有為 virtio
開(kāi)發(fā)過(guò)前端或后端驅(qū)動(dòng)程序,它實(shí)現(xiàn)了一個(gè)有趣的架構(gòu),值得您仔細(xì)去探索。virtio
為提高半虛擬化 I/O 環(huán)境中的效率帶來(lái)了新的機(jī)會(huì),同時(shí)能夠利用 Xen 以前的成果。Linux 不斷地證明它是一個(gè)產(chǎn)品 hypervisor,并且是新虛擬化技術(shù)研究平臺(tái)。virtio
這個(gè)例子展示了將 Linux 用作 hypervisor 的強(qiáng)大之處和開(kāi)放性。
學(xué)習(xí)
virtio
技術(shù)的最好資源。這篇論文詳盡闡述了 virtio
及其內(nèi)部結(jié)構(gòu)。 virtio
背后的秘密就是利用半虛擬化來(lái)改善總體 I/O 性能。要了解使用 Linux 作為 hypervisor 和設(shè)備模擬,請(qǐng)查看 Tim 的文章 “剖析 Linux hypervisor”(developerWorks,2009 年 5 月)和 “Linux 虛擬化和 PCI 透?jìng)骷夹g(shù)”(developerWorks,2009 年 10 月)。virtio
的最重要優(yōu)點(diǎn)之一是在半虛擬化環(huán)境中提升效率。這篇來(lái)自 btm.geek 的博客顯示了 使用 KVM 的 virtio
的優(yōu)勢(shì)。 libvirt
(一個(gè)開(kāi)源虛擬化 API)和 virtio
框架的相似之處。libvirt wiki 展示了如何在 libvirt
中指定 virtio
設(shè)備。virtio
框架的 hypervisor 解決方案:lguest 是一個(gè) x86 hypervisor,它也是由 Rusty Russell 開(kāi)發(fā)的;KVM 是另一個(gè)基于 Linux 的 hypervisor,它是首個(gè)構(gòu)建到 Linux 內(nèi)核中的 hypervisor。virtio
的有趣應(yīng)用之一是開(kāi)發(fā) 共享內(nèi)存消息傳遞,以讓 VM 能夠通過(guò) hypervisor 彼此通信,來(lái)自 SpringerLink 的論文對(duì)此進(jìn)行了闡述。 獲得產(chǎn)品和技術(shù)
討論
M. Tim Jones 是一名嵌入式固件架構(gòu)師,同時(shí)也是 Artificial Intelligence: A Systems Approach, GNU/Linux Application Programming(第二版)、AI Application Programming(第二版)和 BSD Sockets Programming from a Multilanguage Perspective 的作者。他的工程背景包括地球同步航天器內(nèi)核開(kāi)發(fā)、嵌入式系統(tǒng)架構(gòu)和網(wǎng)絡(luò)協(xié)議開(kāi)發(fā)等。Tim 還是科羅拉多州朗蒙特市 Emulex Corp. 的顧問(wèn)工程師。
聯(lián)系客服