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

針對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)

分享創造快樂