1、基本知識
上一篇文章介紹了 virtio 的核心數據的實現和邏輯:linux 之 virtio 子系統核心的數據結構-CSDN博客
virtio 是對半虛擬化 hypervisor?中的一組通用模擬設備的抽象。它允許 hypervisor 導出一組通用的模擬設備,并通過一個通用的應用編程接口(API)讓它們變得可用。下圖展示了為什么這很重要。有了半虛擬化 hypervisor 之后,客戶操作系統能夠實現一組通用的接口,在一組后端驅動程序之后采用特定的設備模擬。后端驅動程序不需要是通用的,因為它們只實現前端所需的行為。
注意,在現實中(盡管不需要),設備模擬發生在使用?QEMU?的空間,因此后端驅動程序與 hypervisor 的用戶空間交互,以通過 QEMU 為 I/O 提供便利。QEMU 是一個系統模擬器,它不僅提供客戶操作系統虛擬化平臺,還提供整個系統(PCI 主機控制器、磁盤、網絡、視頻硬件、USB 控制器和其他硬件元素)的模擬。
2、virtio的模塊層次
2.1 virtio 的驅動的框架
除了前端驅動(在客戶機操作系統中實現)和后端驅動(在虛擬機 hypervisor 中實現)之外,virtio 還定義了 2 個層次來支持客戶機到 hypervisor 的通信。
在頂層(稱為 virtio 層)是虛擬隊列接口,它在概念上將前端驅動程序附加到后端驅動,驅動可以根據需要使用零個或多個隊列。例如,virtio 網絡驅動使用兩個虛擬隊列(一個用于接收,一個用于發送),而 virtio 塊驅動只使用一個隊列。虛擬隊列是虛擬的,它通過 ring 實現,以用于遍歷客戶機到 hypervisor 的信息。這可以以任何方式來實現,只要 Guest 和 hypervisor 通過相同的方式來實現。
內核中實現的virtio 的前端驅動框架:
如圖 所示,列出了五個前端驅動程序,分別用于塊設備(例如磁盤)、網絡設備、PCI 模擬、balloon 驅動(用于動態管理客戶機內存使用)和控制臺驅動。每個前端驅動在 hypervisor 中都有一個對應的后端驅動。
2.2 在驅動中, 前端驅動程序肯定是注冊了以下數據結構的關系
virtio 前端對象層次結構:
從客戶操作系統的角度來看,對象層次結構 的定義如上圖所示。在頂級的是 virtio_driver,它在客戶操作系統中表示前端驅動程序。與該驅動程序匹配的設備由 virtio_device(設備在客戶操作系統中的表示)封裝。這引用?virtio_config_ops?結構(它定義配置 virtio 設備的操作)。virtio_device 由?virtqueue?引用(它包含一個到它服務的 virtio_device 的引用)。最后,每個 virtqueue 對象引用 virtqueue_ops 對象,后者定義處理 hypervisor 的驅動程序的底層隊列操作。
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 指定一個回調函數(查看左 圖中的 virtqueue 結構)。
virtqueue 是一個簡單的結構,它識別一個可選的回調函數(在 hypervisor 使用緩沖池時調用)、一個到 virtio_device 的引用、一個到 virtqueue 操作的引用,以及一個引用要使用的底層實現的特殊 priv 引用。雖然 callback 是可選的,但是它能夠動態地啟用或禁用回調。
virtio緩沖池:客戶操作系統(前端)驅動程序通過緩沖池與 hypervisor 交互。對于 I/O,客戶操作系統提供一個或多個表示請求的緩沖池。例如,你可以提供 3 個緩沖池,第一個表示 Read 請求,后面兩個表示響應數據。該配置在內部被表示為一個散集列表(scatter-gather),列表中的每個條目表示一個地址和一個長度。緩沖區的格式、順序和內容僅對前端和后端驅動程序有意義。內部傳輸(當前實現中的連接點)僅移動緩沖區,并且不知道它們的內部表示。
該層次結構的核心是 virtqueue_ops,它定義在客戶操作系統和 hypervisor 之間移動命令和數據的方式:
第一個函數 add_buf 用于向 hypervisor 提供請求。如前面所述,該請求以散集列表的形式存在。對于 add_buf,客戶操作系統提供用于將請求添加到隊列的 virtqueue、散集列表(地址和長度數組)、用作輸出條目(目標是底層 hypervisor)的緩沖池數量,以及用作輸入條目(hypervisor 將為它們儲存數據并返回到客戶操作系統)的緩沖池數量。當通過 add_buf 向 hypervisor 發出請求時,客戶操作系統能夠通過 kick 函數通知 hypervisor 新的請求。為了獲得最佳的性能,客戶操作系統應該在通過 kick 發出通知之前將盡可能多的緩沖池裝載到 virtqueue。
第二個函數get_buf 處理自 hypervisor 的響應。客戶操作系統僅需調用該函數或通過提供的 virtqueue callback 函數等待通知就可以實現輪詢。當客戶操作系統知道緩沖區可用時,調用 get_buf 返回完成的緩沖區。
最后兩個函數enable_cb 和 disable_cb用來啟用或禁用回調進程(通過在 virtqueue 中由 virtqueue 初始化的 callback 函數)。注意,該回調函數和 hypervisor 位于獨立的地址空間中,因此調用通過一個間接的 hypervisor 來觸發(比如 kvm_hypercall)。
3、virtio? 的驅動實例, 網友寫的比較清楚的virtio-net 的前端驅動程序
Linux virtio-net driver - 知乎
后面找個時間具有看一下源碼。
4、virtio? 的后端程序實例
一定要結合一個后端驅動進行分析,可以對照rpmsg-lite分析