virtio 是一種 I/O 半虛擬化解決方案,是一套通用 I/O 設備虛擬化的程序,是對半虛擬化 Hypervisor 中的一組通用 I/O 設備的抽象。提供了一套上層應用與各 Hypervisor 虛擬化設備(KVM,Xen,VMware等)之間的通訊框架和編程接口,減小跨平臺所帶來的兼容性問題,大大提升驅動程序開發效率。html
在徹底虛擬化的解決方案中,guest VM 要使用底層 host 資源,須要 Hypervisor 來截獲全部的請求指令,而後模擬出這些指令的行爲,這樣勢必會帶來不少性能上的開銷。半虛擬化經過底層硬件輔助的方式,將部分不必虛擬化的指令經過硬件來完成,Hypervisor 只負責完成部分指令的虛擬化,要作到這點,須要 guest 來配合,guest 完成不一樣設備的前端驅動程序,Hypervisor 配合 guest 完成相應的後端驅動程序,這樣二者之間經過某種交互機制就能夠實現高效的虛擬化過程。前端
因爲不一樣 guest 前端設備其工做邏輯大同小異(如塊設備、網絡設備、PCI設備、balloon驅動等),單獨爲每一個設備定義一套接口實屬沒有必要,並且還要考慮擴平臺的兼容性問題,另外,不一樣後端 Hypervisor 的實現方式也大同小異(如KVM、Xen等),這個時候,就須要一套通用框架和標準接口(協議)來完成二者之間的交互過程,virtio 就是這樣一套標準,它極大地解決了這些不通用的問題。linux
歷來賓操做系統的角度來看,對象層次結構 的定義如 圖 4 所示。在頂級的是 virtio_driver
,它在來賓操做系統中表示前端驅動程序。與該驅動程序匹配的設備由 virtio_device
(設備在來賓操做系統中的表示)封裝。這引用 virtio_config_ops
結構(它定義配置 virtio
設備的操做)。virtio_device
由 virtqueue
引用(它包含一個到它服務的 virtio_device
的引用)。最後,每一個 virtqueue
對象引用 virtqueue_ops
對象,後者定義處理 hypervisor 的驅動程序的底層隊列操做。儘管隊列操做是 virtio
API 的核心,我仍是先簡單討論一下新的發現,而後再詳細探討 virtqueue_ops
操做。編程
該流程以建立 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
結構)。api
virtqueue
是一個簡單的結構,它識別一個可選的回調函數(在 hypervisor 使用緩衝池時調用)、一個到 virtio_device
的引用、一個到 virtqueue
操做的引用,以及一個引用要使用的底層實現的特殊 priv
引用。雖然 callback
是可選的,可是它可以動態地啓用或禁用回調。數組
該層次結構的核心是 virtqueue_ops
,它定義在來賓操做系統和 hypervisor 之間移動命令和數據的方式。讓咱們首先探索添加到或從 virtqueue
移除的對象。緩存
從整體上看,virtio 能夠分爲四層,包括前端 guest 中各類驅動程序模塊,後端 Hypervisor (實如今Qemu上)上的處理程序模塊,中間用於先後端通訊的 virtio 層和 virtio-ring 層,virtio 這一層實現的是虛擬隊列接口,算是先後端通訊的橋樑,而 virtio-ring 則是該橋樑的具體實現,它實現了兩個環形緩衝區,分別用於保存前端驅動程序和後端處理程序執行的信息。網絡
嚴格來講,virtio 和 virtio-ring 能夠看作是一層,virtio-ring 實現了 virtio 的具體通訊機制和數據流程。或者這麼理解可能更好,virtio 層屬於控制層,負責先後端之間的通知機制(kick,notify)和控制流程,而 virtio-vring 則負責具體數據流轉發。架構
vring 主要經過兩個環形緩衝區來完成數據流的轉發,以下圖所示。
vring 包含三個部分,描述符數組 desc,可用的 available ring 和使用過的 used ring。
desc 用於存儲一些關聯的描述符,每一個描述符記錄一個對 buffer 的描述,available ring 則用於 guest 端表示當前有哪些描述符是可用的,而 used ring 則表示 host 端哪些描述符已經被使用。
Virtio 使用 virtqueue 來實現 I/O 機制,每一個 virtqueue 就是一個承載大量數據的隊列,具體使用多少個隊列取決於需求,例如,virtio 網絡驅動程序(virtio-net)使用兩個隊列(一個用於接受,另外一個用於發送),而 virtio 塊驅動程序(virtio-blk)僅使用一個隊列。
具體的,假設 guest 要向 host 發送數據,首先,guest 經過函數 virtqueue_add_buf 將存有數據的 buffer 添加到 virtqueue 中,而後調用 virtqueue_kick 函數,virtqueue_kick 調用 virtqueue_notify 函數,經過寫入寄存器的方式來通知到 host。host 調用 virtqueue_get_buf 來獲取 virtqueue 中收到的數據。
存放數據的 buffer 是一種分散-彙集的數組,由 desc 結構來承載,以下是一種經常使用的 desc 的結構:
當 guest 向 virtqueue 中寫數據時,其實是向 desc 結構指向的 buffer 中填充數據,完了會更新 available ring,而後再通知 host。
當 host 收到接收數據的通知時,首先從 desc 指向的 buffer 中找到 available ring 中添加的 buffer,映射內存,同時更新 used ring,並通知 guest 接收數據完畢。
來賓操做系統(前端)驅動程序經過緩衝池與 hypervisor 交互。對於 I/O,來賓操做系統提供一個或多個表示請求的緩衝池。例如,您能夠提供 3 個緩衝池,第一個表示 Read 請求,後面兩個表示響應數據。該配置在內部被表示爲一個散集列表(scatter-gather),列表中的每一個條目表示一個地址和一個長度。
經過 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 是 guest 與 host 之間通訊的潤滑劑,提供了一套通用框架和標準接口或協議來完成二者之間的交互過程,極大地解決了各類驅動程序和不一樣虛擬化解決方案之間的適配問題。
virtio 抽象了一套 vring 接口來完成 guest 和 host 之間的數據收發過程,結構新穎,接口清晰。