什么是协议?
协议是严格的接口定义。
以太网驱动程序发布了符合 ZX_PROTOCOL_ETHERNET_IMPL
的接口。
这意味着它必须提供一组在数据结构中定义的函数
(在本例中为 ethernet_impl_protocol_ops_t
)。
这些函数是所有实现该协议的设备通用的功能,例如, 所有以太网设备都必须提供相应的功能, 界面。
当然,其他协议对其功能有不同的要求
必须提供。
例如,块存储设备将发布一个符合
"代码块实现协议"(ZX_PROTOCOL_BLOCK_IMPL
)和
提供由 block_protocol_ops_t
定义的函数。
此协议包含一个函数,该函数以块为单位返回设备的大小,
示例。
在许多情况下,协议用于简化驱动程序的 接口的通用实现例如,“屏蔽”规则驱动程序实现 通用块接口,并绑定到实现块核心协议的设备, 而“以太网”驱动程序对以太网接口和以太网接口执行相同的操作 协议。有些协议(如本文提到的两种协议)会利用共享内存, 非 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)中,许多驱动程序都在内核中实现。 这意味着它们共享相同的地址空间,并且实际上位于同一个 “process”
这种方法的主要问题是故障隔离 / 利用。 不良驱动程序可能会导致整个内核被删除,因为它位于同一个地址中 因此拥有对所有内核内存和资源的特权访问。 遭到入侵的驱动程序也会出于同样的原因带来安全威胁。
另一种极端做法就是,将每项司机服务都放在自己的部门 被一些微内核操作系统使用。 它的主要缺点是,如果一个司机依赖于另一个司机的服务, 内核必须至少影响一个上下文切换操作(如果不是数据传输) 在两个驱动程序进程之间实现预期目标。 虽然微内核操作系统通常设计为运行速度快, 高频率执行操作是不可取的。
Fuchsia 采用的方法基于驱动程序主机的概念。 驱动程序主机是包含协议栈(即一个或多个 可协同工作的协议。 驱动程序主机从 ELF 共享库(称为动态共享对象、 或 DSO)。
协议栈可有效地创建完整的“驱动程序”用于 由依赖于平台和独立于平台的组件组成的设备; 存储到一个独立的进程容器中
对于高级阅读器,请查看以下 driver dump
命令:
Fuchsia 命令行。它会显示设备树,并向您展示
进程 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 行)
是高级配置与电源接口 (ACPI) 驱动程序,
由 DSO bus-acpi.so
提供。
在主要枚举期间,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。
此外,还创建了一个新的驱动程序主机(进程 ID 2052
),并随
bus-pci.proxy.so
DSO(第 7 行)。
此代理充当从新驱动程序主机 (pid 2052
) 到基础 PCI 的接口
驱动程序 (pid 1416
)。
在此处,我们决定“断开”将设备驱动程序推进到自己的 进程 - 新的驱动程序主机和基础 PCI 驱动程序现在分在两个 不同进程
然后,新的驱动程序主机 2052
会查找匹配的子项 (e1000.so
第 8 行 DSO;之所以将其视为匹配,是因为它具有 ZX_PROTOCOL_PCI
且正确的
VID 和 DID)。
DSO 发布 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
...
请务必注意,节点(设备在 Google Cloud 中
DFv2)没有关联的 .so
文件。而是包含
附加到指定节点的驱动程序的组件清单。