歡迎光臨
每天分享高質量文章

針對Linux的I/O虛擬化框架VirtIO

概而言之,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 的代碼。

 

在完全虛擬化樣式中,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 的右邊是半虛擬化示例。在這裡,來賓操作系統知道它運行在 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 的驅動程式的底層佇列操作。儘管佇列操作是 virtioAPI 的核心,我還是先簡單討論一下新的發現,然後再詳細探討 virtqueue_ops 操作。

 

圖 4. virtio 前端的物件層次結構

 

該流程以創建 virtio_driver 並通過register _virtio_driver 進行註冊開始。virtio_driver 結構定義上層設備驅動程式、驅動程式支持的設備 ID 的串列、一個特性表單(取決於設備型別)和一個回呼函式串列。當 hypervisor 識別到與設備串列中的設備 ID 相匹配的新設備時,將呼叫 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 緩衝池

 

來賓(Guest)操作系統(前端)驅動程式通過緩衝池與 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 的強大之處和開放性。

 

來源:

https://www.ibm.com/developerworks/cn/linux/l-virtio/index.html?from=singlemessage

赞(0)

分享創造快樂