概而言之,virtio
是半虛擬化 hypervisor 中位於裝置之上的抽象層。virtio
由 Rusty Russell 開發,他當時的目的是支援自己的虛擬化解決方案 lguest
。本文在開篇時介紹半虛擬化和類比裝置,然後探索 virtio
的細節。本文的重點是來自 2.6.30 核心發行版的 virtio
架構。
Linux 是 hypervisor 展台。如我的 剖析 Linux hypervisor 所述,Linux 提供各種 hypervisor 解決方案,這些解決方案都有自己的特點和優點。這些方案套件括 Kernel-based Virtual Machine (KVM)、lguest
和 User-mode Linux 等。在 Linux 上配備這些不同的 hypervisor 解決方案會給作業系統帶來負擔,負擔的大小取決於各個解決方案的需求。其中的一項開銷為裝置的虛擬化。virtio
並沒有提供多種裝置類比機制(針對網路、塊和其他驅動程式),而是為這些裝置類比提供一個通用的前端,從而標準化介面和增加代碼的跨平台重用。
完全虛擬化和半虛擬化
加入 My developerWorks 上的綠色小組在 My developerWorks 上的 GReen IT Report 空間 和 綠色計算小組 上討論關於能源、效率和環境的主題並共用資源。
讓我們快速討論一下兩種類型完全不同的虛擬化模式:完全虛擬化和半虛擬化。在完全虛擬化 中,客體作業系統運行在位於物理機器上的 hypervisor 之上。客體作業系統並不知道它已被虛擬化,並且不需要任何更改就可以在該配置下工作。相反,在半虛擬化 中,客體作業系統不僅知道它運行在 hypervisor 之上,還包含讓客體作業系統更高效地過渡到 hypervisor 的代碼(見 圖 1)。
在完全虛擬化模式中,hypervisor必須類比裝置硬體,它是在會話的最低層級進行類比的(例如,網路驅動程式)。儘管在該抽象中類比很乾淨,但它同時也是最低效、最複雜的。在半虛擬化模式中,客體作業系統和 hypervisor 能夠共同合作,讓類比更加高效。半虛擬化方法的缺點是作業系統知道它被虛擬化,並且需要修改才能工作。
圖 1. 在完全虛擬化和半虛擬化環境下的裝置類比
硬體隨著虛擬化技術而不斷改變。新的處理器通過納入進階指令來讓客體作業系統到 hypervisor 的過渡更加高效。此外,硬體也隨著輸入/輸出(I/O)虛擬化而不斷改變(參見 參考資料 瞭解 Peripheral Controller Interconnect [PCI] passthrough 和 single- and multi-root I/O 虛擬化)。
virtio 的替換者
virtio
並不是該領域中的唯一霸主。Xen 提供半虛擬化裝置驅動程式,VMware 也提供 Guest Tools。
但是在傳統的完全虛擬化環境中,hypervisor 必須捕捉這些請求,然後類比物理硬體的行為。儘管這樣做提供很大的靈活性(即運行未更改的作業系統),但它的效率比較低(參見 圖 1 左邊)。圖 1 的右邊是半虛擬化樣本。在這裡,客體作業系統知道它運行在 hypervisor 之上,並包含了充當前端的驅動程式。Hypervisor 為特定的裝置類比實現後端驅動程式。通過在這些前端和後端驅動程式中的 virtio
,為開發類比裝置提供標準化介面,從而增加代碼的跨平台重用率並提高效率。
回頁首
針對 Linux 的抽象
從前面的小節可以看到,virtio
是對半虛擬化 hypervisor 中的一組通用類比裝置的抽象。該設定還允許 hypervisor 匯出一組通用的類比裝置,並通過一個通用的應用編程介面(API)讓它們變得可用。圖 2 展示了為什麼這很重要。有了半虛擬化 hypervisor 之後,客體作業系統能夠實現一組通用的介面,在一組後端驅動程式之後採用特定的裝置類比。後端驅動程式不需要是通用的,因為它們只實現前端所需的行為。
圖 2. virtio 的驅動程式抽象
注意,在現實中(儘管不需要),裝置類比發生在使用 QEMU 的空間,因此後端驅動程式與 hypervisor 的使用者空間互動,以通過 QEMU為 I/O 提供便利。QEMU 是一個系統模擬器,它不僅提供客體作業系統虛擬化平台,還提供整個系統(PCI主機控制器、磁碟、網路、視頻硬體、USB 控制器和其他硬體元素)的類比。
virtio
API 依賴一個簡單的緩衝抽象來封裝客體作業系統需要的命令和資料。讓我們查看 virtio
API 的內部及其組件。
回頁首
Virtio 架構
除了前端驅動程式(在客體作業系統中實現)和後端驅動程式(在 hypervisor 中實現)之外,virtio
還定義了兩個層來支援客體作業系統到 hypervisor 的通訊。在頂級(稱為 virtio)的是虛擬隊列介面,它在概念上將前端驅動程式附加到後端驅動程式。驅動程式可以使用 0 個或多個隊列,具體數量取決於需求。例如,virtio
網路驅動程式使用兩個虛擬隊列(一個用於接收,另一個用於發送),而 virtio
塊驅動程式僅使用一個虛擬隊列。虛擬隊列實際上被實現為跨越客體作業系統和 hypervisor 的銜接點。但這可以通過任意方式實現,前提是客體作業系統和 hypervisor 以相同的方式實現它。
圖 3. vital 架構的進階架構
如 圖 3 所示,分別為塊裝置(比如磁碟)、網路裝置、PCI 類比和 balloon 驅動程式列出了 5 個前端驅動程式。每個前端驅動程式在 hypervisor 中有一個對應的後端驅動程式。
概念階層
從客體作業系統的角度來看,對象階層 的定義如 圖 4 所示。在頂級的是 virtio_driver
,它在客體作業系統中表示前端驅動程式。與該驅動程式匹配的裝置由 virtio_device
(裝置在客體作業系統中的表示)封裝。這引用 virtio_config_ops
結構(它定義配置 virtio
裝置的操作)。virtio_device
由 virtqueue
引用(它包含一個到它服務的 virtio_device
的引用)。最後,每個 virtqueue
對象引用 virtqueue_ops
對象,後者定義處理 hypervisor 的驅動程式的底層隊列操作。儘管隊列操作是 virtio
API 的核心,我還是先簡單討論一下新的發現,然後再詳細探討 virtqueue_ops
操作。
圖 4. virtio 前端的對象階層
該流程以建立 virtio_driver
並通過 register_virtio_driver
進行註冊開始。virtio_driver
結構定義上層裝置驅動程式、驅動程式支援的裝置識別碼 的列表、一個特性表單(取決於裝置類型)和一個回呼函數列表。當 hypervisor 識別到與裝置列表中的裝置識別碼 相匹配的新裝置時,將調用 probe
函數(由 virtio_driver
對象提供)來傳入 virtio_device
對象。將這個對象和裝置的管理資料緩衝起來(以獨立於驅動程式的方式緩衝)。可能要調用 virtio_config_ops
函數來擷取或設定特定於裝置的選項,例如,為 virtio_blk
裝置擷取磁碟的 Read/Write 狀態或設定塊裝置的塊大小,具體情況取決於啟動器的類型。
注意,virtio_device
不包含到 virtqueue
的引用(但 virtqueue
確實引用了 virtio_device
)。要識別與該 virtio_device
相關聯的 virtqueue
,您需要結合使用 virtio_config_ops
對象和 find_vq
函數。該對象返回與這個 virtio_device
執行個體相關聯的虛擬隊列。find_vq
函數還允許為 virtqueue
指定一個回呼函數(查看 圖 4 中的 virtqueue
結構)。
virtqueue
是一個簡單的結構,它識別一個可選的回呼函數(在 hypervisor 使用緩衝池時調用)、一個到 virtio_device
的引用、一個到 virtqueue
操作的引用,以及一個引用要使用的底層實現的特殊 priv
引用。雖然 callback
是可選的,但是它能夠動態地啟用或禁用回調。
該階層的核心是 virtqueue_ops
,它定義在客體作業系統和 hypervisor 之間移動命令和資料的方式。讓我們首先探索添加到或從 virtqueue
移除的對象。
Virtio 緩衝池
客體作業系統(前端)驅動程式通過緩衝池與 hypervisor 互動。對於 I/O,客體作業系統提供一個或多個表示請求的緩衝池。例如,您可以提供3 個緩衝池,第一個表示 Read請求,後面兩個表示響應資料。該配置在內部被表示為一個散集列表(scatter-gather),列表中的每個條目表示一個地址和一個長度。
核心 API
通過 virtio_device
和 virtqueue
(更常見)將客體作業系統驅動程式與 hypervisor 的驅動程式連結起來。virtqueue
支援它自己的由 5 個函數組成的 API。您可以使用第一個函數 add_buf
來向 hypervisor 提供請求。如前面所述,該請求以散集列表的形式存在。對於 add_buf
,客體作業系統提供用於將請求添加到隊列的 virtqueue
、散集列表(地址和長度數組)、用作輸出條目(目標是底層 hypervisor)的緩衝池數量,以及用作輸入條目(hypervisor 將為它們儲存資料並返回到客體作業系統)的緩衝池數量。當通過 add_buf
向 hypervisor 發出請求時,客體作業系統能夠通過 kick
函數通知 hypervisor 新的請求。為了獲得最佳的效能,客體作業系統應該在通過 kick
發出通知之前將儘可能多的緩衝池裝載到 virtqueue
。
通過 get_buf
函數觸發來自 hypervisor 的響應。客體作業系統僅需調用該函數或通過提供的 virtqueue callback
函數等待通知就可以實現輪詢。當客體作業系統知道緩衝區可用時,調用 get_buf
返回完成的緩衝區。
virtqueue
API 的最後兩個函數是 enable_cb
和 disable_cb
。您可以使用這兩個函數來啟用或禁用回調進程(通過在 virtqueue
中由 virtqueue
初始化的 callback
函數)。注意,該回呼函數和 hypervisor 位於獨立的地址空間中,因此調用通過一個間接的 hypervisor 來觸發(比如 kvm_hypercall
)。
緩衝區的格式、順序和內容僅對前端和後端驅動程式有意義。內部傳輸(當前實現中的連接點)僅移動緩衝區,並且不知道它們的內部表示。
回頁首
樣本 virtio 驅動程式
您可以在 Linux 核心的 ./drivers 子目錄內找到各種前端驅動程式的原始碼。可以在 ./drivers/net/virtio_net.c 中找到 virtio
網路驅動程式,在 ./drivers/block/virtio_blk.c 中找到 virtio
塊驅動程式。子目錄 ./drivers/virtio 提供 virtio
介面的實現(virtio
裝置、驅動程式、virtqueue
和連接點)。virtio
還應用在 High-Performance Computing (HPC) 研究中,以開發出通過共用記憶體傳遞的 inter-virtual machine (VM) 通訊。尤其是,這是通過使用 virtio
PCI 驅動程式的虛擬化 PCI 介面實現的。您可以在 參考資料 部分更多地瞭解這個知識點。
現在,您可以在 Linux 核心中實踐這個半虛擬化基礎架構。您所需的包括一個充當 hypervisor 的核心、一個來賓操作性核心和用於裝置類比的 QEMU。您可以使用 KVM(位於主機核心中的一個模組)或 Rusty Russell 的 lguest
(修改版的 Linux 客體作業系統核心)。這兩個虛擬化解決方案都支援 virtio
(以及用於系統類比的 QEMU 和用於虛擬化管理的 libvirt
)。
Rusty 的 lguest
是針對半虛擬化驅動程式和更快速地類比虛擬設備的更簡潔程式碼程式庫。但更重要的是,實踐證明 virtio
比現有的商業解決方案提供更出色的效能(網路 I/O 能夠提升 2-3 倍)。效能的提升是需要付出代價的,但是如果您使用 Linux 作為 hypervisor 和客體作業系統,那麼這樣做是值得的。
回頁首
結束語
也許您可能從來沒有為 virtio
開發過前端或後端驅動程式,它實現了一個有趣的架構,值得您仔細去探索。virtio
為提高半虛擬化 I/O 環境中的效率帶來了新的機會,同時能夠利用 Xen 以前的成果。Linux 不斷地證明它是一個產品 hypervisor,並且是新虛擬化技術研究平台。virtio
這個例子展示了將 Linux 用作 hypervisor 的強大之處和開放性。
參考資料
學習
- Rusty Russell 的 “Virtio:towards a de factor standard for virtual I/O devices” 是深入瞭解
virtio
技術的最好資源。這篇論文詳盡闡述了 virtio
及其內部結構。
- 這篇文章談論兩個虛擬化機制:完全虛擬化和半虛擬化。要更多地瞭解 Linux 中的各種虛擬化機制,請查看 Tim 的文章 “虛擬 Linux”(developerWorks,2006 年 12 月)。
virtio
背後的秘密就是利用半虛擬化來改善總體 I/O 效能。要瞭解使用 Linux 作為 hypervisor 和裝置類比,請查看 Tim 的文章 “剖析 Linux hypervisor”(developerWorks,2009 年 5 月)和 “Linux 虛擬化和 PCI 透傳技術”(developerWorks,2009 年 10 月)。
- 本文討論裝置類比,提供該功能的最重要應用程式之一是 QEMU(一個系統模擬器)。您可以閱讀 Tim 的文章 “使用 QEMU 進行系統模擬”(developerWorks,2007 年 9 月),更多地瞭解 QEMU。
- Xen 還包含虛擬化驅動程式的概念。Paravirtual WindowsDrivers 討論半虛擬化和硬體輔助虛擬化(HVM),尤其是後者。
virtio
的最重要優點之一是在半虛擬化環境中提升效率。這篇來自 btm.geek 的部落格顯示了 使用 KVM 的 virtio
的優勢。
- 本文討論
libvirt
(一個開源虛擬化 API)和 virtio
架構的相似之處。libvirt wiki 展示了如何在 libvirt
中指定 virtio
裝置。
- 這篇文章討論兩個利用
virtio
架構的 hypervisor 解決方案:lguest 是一個 x86 hypervisor,它也是由 Rusty Russell 開發的;KVM 是另一個基於 Linux 的 hypervisor,它是首個構建到 Linux 核心中的 hypervisor。
virtio
的有趣應用之一是開發 共用記憶體訊息傳遞,以讓 VM 能夠通過 hypervisor 彼此通訊,來自 SpringerLink 的論文對此進行了闡述。