Uniform Driver Interface

来自osdev
跳到导航 跳到搜索


Logo
官方UDI项目标志

UDI复兴计划在Freenode(irc.freenode.net)上维护一个名为#udi的IRC频道。 欢迎加入并提出问题。

UDI代表“统一驱动程序接口(Uniform Driver Interface)”。 它是框架和驱动程序API/ABI的规范,使不同的操作系统(实现UDI框架)能够使用相同的驱动程序。 由几家大型工业公司构思,尽管它具有功能性并兑现了其承诺,但它已经处于休眠状态。

UDI驱动程序在同一CPU系列上运行的所有符合UDI的操作系统中都是二进制兼容的。 它们在所有实现UDI的操作系统上也是源代码兼容的。 这意味着,驱动程序只需开发一次。

微软Windows可以获得他们想要的所有硬件驱动程序,但GNU出于他们的哲学理由不鼓励UDI(译者注:好像主要认为不开源自由的软件可进入自由社区,而GNU的自由软件又被商业系统无偿使用,这种不对等不公平),而对于业余操作系统开发人员来说,它的优势是显而易见的。

为什么选择UDI?

如果被广泛采用,统一的驱动程序接口将为跨内核和平台的实现提供通用的驱动程序框架,从而使驱动程序能够在不考虑目标内核以及在很大程度上不考虑目标硬件平台的情况下提供。 与现有的驱动程序接口相比,UDI有几个突出的优势,这可能会促使读者采用它:

优势

  • 上节中提到的可移植性 (跨操作系统和跨平台) 可能是首先开发UDI的主要关注点。 我们所能希望的是,有足够多的操作系统支持该模型,以便我们能够真正利用它。
  • 对于原生UDI实现,性能与自定义原生API驱动程序相当或更好。 对于性能至关重要的环境,UDI不会抑制服务质量。 UDI被明确设计为非阻塞和无锁的,具有一个同步模型,可以在不锁定的情况下提高MP可伸缩性,以及许多其他以高可伸缩性为重点的功能。
  • UDI可以无缝集成到现有内核环境中,而无需考虑操作系统架构(单内核微内核、POSIX与非POSIX等)几乎没有额外的性能开销。
  • 设计明确提供了可靠性和稳定性。 UDI试图消除某些类型的潜在缺陷,例如(但不限于)资源泄漏和死锁(所有接口都可以在根本不锁定的情况下实现)。
  • 灵活性是UDI设计时考虑到的另一件事:不仅是在构思规范的方式上(即可扩展), 但也从某种意义上说,它允许系统程序员在需要时应用驱动程序隔离、影子驱动等技术。
  • 接口在各个方面都是完全异步的;高扩展系统正变得越来越占主导地位,异步性正慢慢成为现代内核“理所当然”的特性。 UDI走在了前列,使兼容的内核能够慢慢采用异步接口,而不必稍后进行重大重新设计。

缺点

  • 一定的复杂性,通常需要一段时间才能理解规范。
  • 不能简单移植到未成熟的内核 内核必须具有一定的最低成熟度,才能合理地尝试使其符合UDI。
  • 对于“临时性”项目来说根本不可行:需要大量的先见之明和前期工作。

UDI驱动程序的核心组件

Environment
UDI环境的高层视图

统一驱动程序接口规范的实现称为UDI环境。 有一个可用的参考实现(参见下面的链接),它为几个现有内核(Linux、BSD、Solaris)提供了可用的代码,可以将其用作新实现的基础。 内核环境实现负责提供UDI标准规定的服务调用接口; 内核可以选择将它们作为本机系统调用或通过库扩展来实现-决定权取决于实现者。 UDI范例可以识别两种类型的服务调用: 同步(将立即返回给调用者,即返回给驱动程序)和异步(通过回调机制工作)。

UDI驱动程序还积极参与识别其子设备并帮助构建主机内核的设备树。 总线驱动程序枚举下级内容,等等。 这些总线中的每一条都可能连接有几个控制器。 这些设备的UDI驱动程序将像硬件一样以树状方式进行交互。 让我们仔细看看驱动本身吧!

驱动程序被分成一个或多个模块,每个模块至少有一个区域。 已经实例化 (可以这么说) 的驱动程序使用IPC调用 (“通道操作”) 在模块和区域之间进行通信。 如果一个驱动程序用于实例化多个设备(例如,一个磁盘驱动程序用于实例化两个单独的磁盘设备),那么实际的驱动程序代码是使用写时复制(Copy-on-Write)映射,还是在内存中复制等,取决于环境。

模块(Modules)

模块本质上是单个可执行代码对象。 具体来说,驱动程序可以分解为多个可执行文件。 可以将可能只需要加载某些组件并且可能不需要所有时间都将其所有代码放在存储器中的大型驱动程序实现为多模块驱动程序。 当然,将驱动程序代码划分为模块取决于驱动程序供应商。 大多数UDI驱动程序预计为单模块驱动程序,但图形卡驱动程序等复杂驱动程序最好作为多模块驱动程序实现。 例如,如果图形驱动程序将OpenGL 3D API与Direct3D API一起导出,则很可能两个前后端都有大量代码,如果同时加载这些代码将占用大量内存。 大多数内核将要么使用OpenGL要么使用Direct3D,因此,图形驱动程序可以将其OpenGL和Direct3D实现拆分为单独的模块, 这将使加载该驱动程序的内核能够避免为它不使用的API的代码和数据分配内存。

区域(Regions)

正文:[[User:Gravaera/UDI_Regions | UDI Regions]

区域只不过是相关数据块。 例如,网卡可能是具有特定于其send()函数的一组寄存器状态, 以及特定于其receive() 函数的一组不同的统计和变量。 在UDI中,数据被明确划分为多个功能区域。 区域只不过是驱动程序为其状态变量分配的数据。 将驱动程序数据拆分为所讨论设备的功能子组件的最直观的方法。 因此,网卡驱动程序可以选择一个发送区域和一个接收区域。 图形驱动程序写入器可以选择将驱动程序划分为帧缓冲器写入区域、变换区域等。 然后,可以根据该区域的目的,通过UDI IPC信道将IPC请求消息发送到每个区域。

区域也构成UDI中并发执行的单元。 由于区域只不过是数据,因此它们也是必须针对并发写入进行同步的单元。 通常,这意味着确保没有两个线程可以一次修改区域数据。 UDI接口的设计完全可以在不使用锁定的情况下工作,由主机操作系统环境选择是否使用无锁算法、自旋锁、等待队列或其他方法,以确保没有两个线程同时修改区域数据。 有关几个实用的UDI同步模型的详细说明,请参阅以上正文链接。

UDI区域的另一个属性是它们与位置和实例无关,这意味着它们可以从一个地方移动到另一个地方,而不会影响任何其他区域,因为它们没有共同的状态。 也就是说,可以通过网络将驱动程序编组并从一个NUMA节点移动到另一个NUMA节点,或从一台物理机器移动到另一台物理机器,或任何其他类似类型的迁移。 这在多处理器系统 (esp. NUMA) 和高规模计算集群中特别有趣,因为由于性能和资源限制,环境可能分离区域。 值得一提的是,由于独立状态不同,各区域执行的任务是相互排斥的 (例如,网络驱动程序可能有一个处理数据包发送和另一个接收的区域)。 这是一个潜在的领域,主机(Host)操作系统可以进行巨大的优化以消除性能瓶颈。

通道(Channels)

正文: UDI Channels

区域沟通的唯一途径是通过通道。 通道是双向通信机制的不可知抽象。 两个通道端点中的每一个都提供操作向量,该操作向量是一组入口点。 它们通过udi_channel_t类型的句柄引用 (检查下面的句柄定义)。 通道操作以及相关功能由元语言定义。 元语言是为每类驱动程序单独定义的,但我们很快就会讲到这一点。

元语言(Metalanguages)

Main article: Metalanguages

元语言为各种目的定义了核心规范的扩展,还可用于定义模块/区域之间的自定义IPC协议API。 可能需要定制协议API的情况的一个示例是,例如,网卡驱动程序具有从内核获取用于电源管理的命令(“Go to sleep”、“prepare to shutdown”等)的Control区域,然后它具有Send() 区域和Receive() 区域,这两个区域分别处理其send() 和 receive()函数。

自然而然地,如果驱动程序在其控制区域上从内核接收到 “Go to sleep” 命令,则需要向其发送和接收区域发送消息以使其停止操作。 没有为跨UDI通道的IPC定义通用IPC_Send()函数 -- 所有IPC必须根据元语言定义的协议API进行,无论是由UDI规范标准化还是自定义。 值得庆幸的是,驱动程序编写者不需要为他们想要在区域之间简单地发送自定义消息的每一种情况定义自定义协议: UDI核心规范定义了一个“通用I/O元语言”IPC协议API,它涵盖了广泛的通用IPC需求,并可以根据需要使用自定义消息进行扩展。

除了API/IPC协议之外,元语言还涵盖核心规范的扩展。 例如,可以根据需要扩展已经定义的UDI总线/桥元语言以支持新总线; PCI总线驱动程序、ISA总线驱动程序等并不都需要新的元语言,因为UDI已经定义了一种核心的UDI总线/桥接元语言。 该核心UDI总线/网桥元语言可以使用特定于每条总线的总线/网桥元语言扩展来扩展。 在这种情况下,UDI标准已经定义了元语言,并且该元语言本身根据需要针对每条总线进行了扩展。

必要时还可以创建全新的元语言; 例如,SCSI主机总线适配器不是真正的总线,但它是充当SCSI设备(主要是磁盘)父设备的I/O微控制器设备。 它看起来像总线,但不是真正的总线,并且可以更好地使用IPC协议和自己的API来处理。 因此,UDI规范定义了一个SCSI主机总线适配器元语言API,用于管理SCSI外围设备(磁盘)和SCSI主机总线适配器之间的通信(IPC)。 在任何给定的主板上,在下面的ASCII领域中常见的排列可以如下所示。 SCSI HBA不是总线,SCSI磁盘和SCSI HBA之间的IPC通信不能被限制为遵循与总线及其子设备之间的通信相同的格式。 在这种情况下,一个用于通信的新元语言API是一个好主意。

值得一提的是,还可以仅使用UDI通用I/O元语言在SCSI磁盘及其父SCSI HBA之间进行通信 -- 通用I/O元语言也同样适用于该目的。

RootNode
|- PCI-Bus-0
|  |- ...
|  +- ...
|
|- PCI-Bus-1
|  +- SCSI HBA
|     |- SCSI-Peripheral-0 (disk)
|     +- SCSI-Peripheral-1 (disk)
|
+- PCI-Bus-2

元语言本质上是UDI IPC通道协议定义或API定义,以及核心规范的扩展定义。 因此得名:Meta-Languages

驱动程序配置

对于UDI驱动程序的静态属性,有一种特殊的配置方法,使用名为udiprops的文件。txt。 此文件在源代码分发的每个驱动程序包中独立分发,并链接到二进制分发的特殊部分(称为.udiprops)。

udiprops.txt文件不仅允许静态配置选项,而且还用于UDI驱动程序的构建过程中,因为它们不使用makefiles-并不是说它在技术上不可行。 为了简单起见,UDI规范定义了用于构建、打包和安装UDI驱动程序的工具,因为与POSIX工具不同,它们不需要操作系统具有任何额外功能(例如VFS)。 幸运的是,参考实现中提供了这些工具,您所需要做的就是构建它们。

下面你可以看到一个示例udiprops.txt:

  properties_version 0x101
  
  message 1 Project UDI
  message 2 http://www.project-UDI.org/participants.html
  message 3 Pseudo-Driver
  message 4 Generic UDI Pseudo-Driver
  release 3 1.01
  
  supplier	1
  contact	2
  name		3
  shortname	pseudod
  
  ##
  ## Interface dependencies
  ##
  requires udi	 	0x101
  requires udi_gio 	0x101
  
  ##
  ## Build instructions.
  ##
  
  module pseudod
  compile_options -DPSEUDO_GIO_META=1
  source_files pseudo.c pseudo.h
  region 0
  
  ##
  ## Metalanguage usage
  ##
  
  meta 1 udi_gio		# Generic I/O Metalanguage
  
  child_bind_ops 1 0 1		# GIO meta, primary region, ops_index 1
  
  # Orphan driver; no device line
  
  #
  # Initialization, shutdown messages
  #
  message 1100  pseudod: devmgmt_req %d
  message 1500  pseudod: final_cleanup_req

当然udiprops.txt可能比这个复杂得多,我只想让你看看它是什么样子。 您应该检查所有编译选项、语句和配置选项的规范。

编程模型

所有UDI函数调用本质上都是异步的;这意味着它们不会隐式阻塞。 “兼容”的UDI驱动程序将始终是隐式非阻塞的。 “主机内核(host kernel)” 是否支持非阻塞编程模型取决于该内核,对于任何特定内核,可能需要使用锁定,互斥和阻塞。 当然,对于一个完全支持非阻塞异步模型的内核,UDI只需无缝扩展即可。

由于其异步特性,UDI驱动程序在很大程度上类似于服务器,并且具有非常好的吞吐量,这是因为驱动程序本身只有在主机内核对其施加限制的情况下才会挡路。 对于没有扩展限制的主机内核,UDI驱动程序也将天生地不受限制地扩展--“兼容” UDI驱动程序的吞吐量仅取决于主机内核的限制。

UDI驱动程序不会隐式假设使用锁定、阻塞或任何特定的线程或同步模型。 它们非常适合任何类型的宿主环境。 因此,UDI规范没有定义任何锁定操作。 主机内核完全可以无锁地运行UDI驱动程序。

驱动程序故障

当环境检测到非法行为时,行为不当的区域通常会被区域杀死,所有相邻区域都会被通知。 通往该地区的所有渠道将被关闭,该地区拥有的所有资源将被释放。

另见

现有实现

外部链接