什麼是通訊協定?
通訊協定是一種嚴格的介面定義。
乙太網路驅動程式庫發布了與 ZX_PROTOCOL_ETHERNET_IMPL
相符的介面。這表示必須提供資料結構中定義的一組函式 (在本例中為 ethernet_impl_protocol_ops_t
)。
這些函式適用於所有實作通訊協定的裝置。舉例來說,所有乙太網路裝置都必須提供可查詢介面 MAC 位址的函式。
其他通訊協定當然對其必須提供函式有不同的要求。舉例來說,區塊裝置會發布符合「區塊實作通訊協定」(ZX_PROTOCOL_BLOCK_IMPL
) 的介面,並提供 block_protocol_ops_t
定義的函式。例如,這個通訊協定含有會傳回裝置區塊大小的函式。
在許多情況下,通訊協定會使用通訊協定的常見實作,讓驅動程式更簡單。舉例來說,「block」驅動程式庫會實作通用區塊介面,並繫結至實作 Block Core 通訊協定的裝置,而「乙太網路」驅動程式庫也會對乙太網路介面和乙太網路通訊協定執行相同作業。有些通訊協定 (例如此處引用的兩個通訊協定) 使用共用記憶體和非 RPC 訊號,因此使用效率更高、延遲時間更短,處理量也優於其他通訊協定。
類別代表裝置實作介面或通訊協定的承諾。裝置檔案系統中會依照邏輯路徑 (例如 /sys/platform/pci/00:02:00/e1000
) 存放裝置。如果這些類別是特定類別,也會在 /dev/class/CLASSNAME/...
下方顯示為別名。e1000
驅動程式庫會實作 Ethermac 介面,因此也會顯示在 /dev/class/ethermac/000
中。類別目錄內的名稱不可重複,但系統會依需求指派。
通訊協定範例:
- PCI 根通訊協定 (
ZX_PROTOCOL_PCIROOT
), - PCI 裝置通訊協定 (
ZX_PROTOCOL_PCI
),以及 - 乙太網路實作通訊協定 (
ZX_PROTOCOL_ETHERNET_IMPL
)。
括號中的名稱是與通訊協定對應的 C 語言常數,僅供參考。
各平台 (與平台無關)
如上所述,我們曾提到 ZX_PROTOCOL_ETHERNET_IMPL
與用戶端使用的函式「非常接近」,但移除了一個步驟。這是因為用戶端和驅動程式庫之間還有一個通訊協定 (ZX_PROTOCOL_ETHERNET
)。這個額外的通訊協定應用於處理所有乙太網路驅動程式通用的功能 (避免程式碼重複)。包括緩衝區管理、狀態回報和管理功能。
這實際上是「與平台有關」和「平台獨立」的分離;一般程式碼會存在於平台的獨立部分 (一次),而且驅動程式庫專屬程式碼是在平台獨立元件中實作。
這個架構會重複用於多個位置。如使用區塊裝置,則硬體驅動程式庫會繫結至匯流排 (例如PCI) 並提供 ZX_PROTOCOL_BLOCK_IMPL
通訊協定。平台獨立的驅動程式庫會繫結至 ZX_PROTOCOL_BLOCK_IMPL
,並發布用戶端端的通訊協定 ZX_PROTOCOL_BLOCK
。
螢幕控制器、I2C 匯流排和序列驅動程式也搭配運作。
程序 / 通訊協定對應
為簡化上述討論,我們並未討論程序區隔,因為這與驅動程式有關。為了瞭解這些問題,我們來看看其他作業系統如何處理這些問題,並與 Fuchsia 做法做比較。
在 Linux 等單體式核心中,許多驅動程式會在核心內實作。 這意味著這些儲存空間共用的位址空間相同,而且實際上會存放在相同的「程序」中。
這種方法的主要問題是錯誤隔離 / 利用。不良的驅動程式庫會佔用整個核心,因為此核心位於相同的位址空間,因此有權存取所有核心記憶體和資源。就算驅動程式庫遭到入侵,也會基於相同原因帶來安全性威脅。
另一個極端做法,就是將每項和每個驅動程式庫服務都放到各自的程序中,一些微核心作業系統會使用。其主要的缺點是,如果其中一個驅動程式庫依賴另一個驅動程式庫的服務,則核心必須在兩個驅動程式庫程式程序之間至少效果切換作業 (如果不能同時進行資料移轉)。雖然微核心作業系統通常設計為在這類作業中快速運作,但以高頻率執行並不理想。
Fuchsia 採取的做法是以驅動程式代管程序的概念為基礎。驅動程式代管程序是一個包含通訊協定堆疊的程序,也就是一或多個搭配運作的通訊協定。驅動程式主機會從 ELF 共用資料庫 (稱為動態共用物件或 DSO) 載入驅動程式。
通訊協定堆疊可有效地在獨立的程序容器中,為裝置建立完整的「驅動程式庫」,其中包含與平台相依和平台的獨立元件。
對於進階讀取器,請查看 Fuchsia 指令列提供的 driver dump
指令。這項工具會顯示裝置的樹狀結構,並顯示程序 ID、DSO 名稱和其他實用資訊。
以下是只顯示 PCI 乙太網路驅動程式庫零件的高度編輯版本:
1. [root]
2. [sys]
3. <sys> pid=1416 /boot/driver/bus-acpi.so
4. [acpi] pid=1416 /boot/driver/bus-acpi.so
5. [pci] pid=1416 /boot/driver/bus-acpi.so
...
6. [00:02:00] pid=1416 /boot/driver/bus-pci.so
7. <00:02:00> pid=2052 /boot/driver/bus-pci.proxy.so
8. [e1000] pid=2052 /boot/driver/e1000.so
9. [ethernet] pid=2052 /boot/driver/ethernet.so
如上所述,您可以看到程序 ID 1416
(第 3 到 6 行) 是 DSO bus-acpi.so
實作的進階設定和電源介面 (ACPI) 驅動程式庫。
在主要列舉期間,ACPI DSO 偵測到 PCI 匯流排。這會導致系統發布含有 ZX_PROTOCOL_PCI_ROOT
的父項 (第 5 行,造成 [pci]
項目出現),進而導致驅動程式代管程序載入 bus-pci.so
DSO 並繫結至該父項。這個 DSO 是我們在上述討論中提到的「基本 PCI 驅動程式庫」。
在繫結期間,底層 PCI 驅動程式列舉了 PCI 匯流排,找到了乙太網路卡 (第 6 行可偵測公車 0,裝置 2,函式 0,如 [00:02:00]
)。(當然,還有許多其他裝置也會找到,但我們已從上述資訊中移除,以求簡單起見)。
偵測這部裝置後,基本 PCI 驅動程式庫會發布含有 ZX_PROTOCOL_PCI
和裝置的 VID 和 DID 的新父項。此外,已透過 bus-pci.proxy.so
DSO (第 7 行) 建立新的驅動程式代管程序 (程序 ID 2052
)。這個 Proxy 可做為新驅動程式代管程序 (pid 2052
) 至基礎 PCI 驅動程式庫 (pid 1416
) 的介面。
這時我們做出決定,就要將裝置驅動程式庫「永久」納入自己的程序:新的驅動程式代管程序和基礎 PCI 驅動程式庫現在分為兩個不同的程序。
新的驅動程式代管程序 2052
會找到相符的子項 (第 8 行上的 e1000.so
DSO;由於其有 ZX_PROTOCOL_PCI
和正確的 VID 和 DID,因此系統視為相符)。DSO 會發布 ZX_PROTOCOL_ETHERNET_IMPL
,該 ZX_PROTOCOL_ETHERNET_IMPL
會繫結至相符的子項 (第 9 行上的 ethernet.so
DSO;由於其具有 ZX_PROTOCOL_ETHERNET_IMPL
通訊協定,因此視為相符)。
這個鏈結不會顯示的原因是最終 DSO (ethernet.so
) 會發布 ZX_PROTOCOL_ETHERNET
,也就是客戶可以使用的元件,因此不會另外加入「裝置」繫結。
驅動程式架構第 2 版 (DFv2)
如果啟用了驅動程式庫架構第 2 版,driver dump
會顯示稍微不同的樹狀結構。
$ driver dump
[root] pid=4766 fuchsia-boot:///#meta/platform-bus.cm
[sys] pid=4766
[platform] pid=4766
[pt] pid=4766 fuchsia-boot:///#meta/platform-bus-x86.cm
[acpi] pid=4766
[acpi-pwrbtn] pid=4766 fuchsia-boot:///#meta/hid.cm
...
[PCI0] pid=4766 fuchsia-boot:///#meta/bus-pci.cm
[bus] pid=4766
...
[00_04_0] pid=4766 fuchsia-boot:///#meta/virtio_ethernet.cm
[virtio-net] pid=4766 fuchsia-boot:///#meta/netdevice-migration.cm
[netdevice-migration] pid=4766 fuchsia-boot:///#meta/network-device.cm
[network-device] pid=4766
...
請務必指出,節點 (裝置在 DFv2 中稱為節點) 並未擁有相關聯的 .so
檔案。相反地,使用附加至指定節點的驅動程式庫元件資訊清單網址。