设备驱动程序需要作为操作系统的组成部分透明地工作。理解内核工作方式是了解设备驱动程序的前提条件。本章概述了 Solaris 内核和设备树。有关设备驱动程序工作方式的概述,请参见第 1 章。
本章介绍有关以下主题的信息:
Solaris 内核是用于管理系统资源的程序。内核将应用程序与系统硬件隔离,并为它们提供基本系统服务,如输入/输出 (input/output, I/O) 管理、虚拟内存和调度。内核由需要时动态装入内存的对象模块组成。
Solaris 内核在逻辑上可分为两个部分: 第一部分称为内核,用于管理文件系统、调度和虚拟内存。第二部分称为 I/O 子系统,用于管理物理组件。
内核提供了一组接口,供可通过系统调用访问的应用程序使用。Reference Manual Collection 的第 2 部分对系统调用进行了介绍(请参见 Intro(2))。某些系统调用用于调用设备驱动程序以执行 I/O 操作。设备驱动程序是可装入的内核模块,用于管理数据传输,同时将内核的其余部分与设备硬件隔离。为了与操作系统兼容,设备驱动程序需要能够提供多线程、虚拟内存寻址以及 32 位和 64 位操作之类的功能。
下图解释了内核的工作机制。内核模块用于处理来自应用程序的系统调用。I/O 模块用于与硬件通信。
内核通过以下功能提供对设备驱动程序的访问:
设备至驱动程序映射。内核将维护设备树。树中的每个节点都表示一个虚拟设备或物理设备。内核通过将设备节点名称与系统中安装的驱动程序集进行匹配,从而将每个节点绑定到驱动程序。仅当存在驱动程序绑定时,应用程序才能访问设备。
DDI/DKI 接口。DDI/DKI(Device Driver Interface/Driver-Kernel Interface,设备驱动程序接口/驱动程序内核接口)接口可对驱动程序和内核、设备硬件以及引导/配置软件之间的交互进行标准化。这些接口使驱动程序独立于内核,并且改进了驱动程序在相同体系架构下不同操作系统版本间的可移植性。
LDI。LDI(Layered Driver Interface,分层驱动程序接口)是 DDI/DKI 的扩展。LDI 允许内核模块访问系统中的其他设备。LDI 还允许确定内核当前使用的设备。请参见第 14 章。
Solaris 内核是多线程的。在多处理器计算机上,多个内核线程可以运行内核代码并且可以并发运行。内核线程也可能随时被其他内核线程抢先。
内核的多线程特征对设备驱动程序强加了某些附加限制。有关多线程注意事项的更多信息,请参见第 3 章。必须对设备驱动程序进行编码,使其在许多不同线程请求时按需运行。对于每个线程,驱动程序必须处理重叠的 I/O 请求的争用问题。
Solaris 虚拟内存系统的完整概述超出本书范围,但在讨论设备驱动程序时使用了两个特别重要的虚拟内存术语: 虚拟地址和地址空间。
虚拟地址。虚拟地址是由内存管理单元 (memory management unit, MMU) 映射到物理硬件地址的地址。驱动程序可直接访问的所有地址都属于内核虚拟地址。内核虚拟地址引用内核地址空间。
地址空间。地址空间是一组虚拟地址段。每个地址段都是一个连续范围的虚拟地址。每个用户进程都拥有一个称为用户地址空间的地址空间。内核拥有其自己的地址空间,称为内核地址空间。
设备在文件系统中表示为特殊文件。在 Solaris OS 中, 这些文件驻留在 /devices 目录分层结构中。
特殊文件的类型可以为块,也可以为字符。该类型表示了设备驱动程序的种类。驱动程序可以实现这两种类型。例如,磁盘驱动程序导出字符接口以供 fsck(1) 和 mkfs(1) 实用程序使用,导出块接口以供文件系统使用。
每个特殊文件都与一个设备编号 (dev_t) 关联。设备编号由主设备号和次要设备号组成。主设备号标识与特殊文件关联的设备驱动程序。次要设备号由设备驱动程序创建,供其用来进一步标识特殊文件。通常,次要设备号是一种编码,用于标识驱动程序应访问的设备实例以及应执行的访问类型。例如,次要设备号可以标识用于备份的磁带设备,并可指定完成备份操作后需要将磁带反绕。
在 System V Release 4 (SVR4) 中,设备驱动程序与 UNIX 内核其余部分之间的接口被标准化为 DDI/DKI。DDI/DKI 在 Reference Manual Collection 的第 9 部分中进行介绍。第 9E 节介绍驱动程序入口点,第 9F 节介绍驱动程序可调用的函数,而第 9S 节介绍设备驱动程序使用的内核数据结构。请参见 Intro(9E)、Intro(9F) 和 Intro(9S)。
DDI/DKI 旨在对设备驱动程序与内核其余部分之间的所有接口进 行标准化并进行说明。此外,无论处理器体系结构是 SPARC 还是 x86,DDI/DKI 都允许任何运行 Solaris OS 的计算机的驱动程序的源代码和二进制代码保持兼容。仅使用 DDI/DKI 中包含的内核功能的驱动程序称为与 DDI/DKI 兼容的设备驱动程序。
DDI/DKI 允许您为运行 Solaris OS 的任何计算机编写与平台无关的设备驱动程序。通过这些二进制代码兼容的驱动程序,您可以更方便地将第三方硬件和软件集成到运行 Solaris OS 的任何计算机中。DDI/DKI 与体系结构无关,从而允许同一驱动程序在一组不同的计算机体系结构中工作。
平台无关性是通过在以下方面设计 DDI 实现的:
动态装入和卸载模块
电源管理
中断处理
从内核或用户进程访问设备空间,即寄存器映射和内存映射
使用 DMA 服务从设备访问内核或用户进程空间
管理设备属性
Solaris OS 中的设备表示为互连的设备信息节点树。设备树描述特定计算机的已装入设备的配置。
系统将会生成树结构,其中包含有关引导时连接到计算机的设备的信息。此外,系统正常运行时也可以动态重新配置设备树。设备树从表示平台的根设备节点开始。
根节点下面是设备树的分支。分支由一个或多个总线结点设备和一个终止叶设备组成。
总线结点设备可为设备树中的从属设备提供总线映射和转换服务。PCI - PCI 网桥、PCMCIA 适配器和 SCSI HBA 都是结点设备的示例。编写结点设备驱动程序的讨论仅限于 SCSI HBA 驱动程序的开发(请参见第 18 章)。
叶设备通常为外围设备,如磁盘、磁带、网络适配器、帧缓存器等。叶设备驱动程序可以导出传统的字符驱动程序接口和块驱动程序接口。通过这些接口,用户进程可在存储设备或通信设备中读取和写入数据。
系统通过以下步骤来生成树:
CPU 经过初始化后搜索固件。
主要固件(OpenBoot、基本输入/输出系统 (Basic Input/Output System, BIOS) 或 Bootconf)初始化并创建包含已知或自标识硬件的设备树。
当主要固件在设备中发现兼容固件时,主要固件将初始化该设备并检索设备属性。
该固件将查找并引导操作系统。
内核从树的根节点开始,搜索匹配的设备驱动程序并将该驱动程序绑定到设备。
如果设备是结点,则内核会查找固件尚未检测到的子设备。内核会将所有子设备都添加到树的子树节点下面。
内核从步骤 5 开始重复该过程,直到无需再创建设备节点。
每个驱动程序都会导出设备操作结构 dev_ops(9S),以定义设备驱动程序可以执行的操作。设备操作结构包含通用操作(如 attach(9E)、detach(9E) 和 getinfo(9E))的函数指针。该结构同时还包含了一组与特定总线结点驱动程序操作相关的函数指针,以及一组与特定叶结点设备驱动程序操作相关的函数指针。
树结构将在节点之间创建父子关系。此父子关系是体系结构无关性的关键。当叶驱动程序或总线结点驱动程序本质上需要依赖于体系结构的服务时,该驱动程序会请求其父级提供该服务。采用此方法,不管计算机或处理器的体系结构是什么,驱动程序都可以正常运行。下图显示了典型的设备树。
子树节点可以有一个或多个子节点。叶节点表示各个设备。
libdevinfo 库提供访问设备树内容的编程接口。
prtconf(1M) 命令显示设备树的完整内容。
/devices 分层结构是设备树的表示形式。使用 ls(1) 命令查看该分层结构。
/devices 仅显示将驱动程序配置到系统中的设备。prtconf(1M) 命令显示所有设备节点,而不管系统中是否存在设备驱动程序。
libdevinfo 库提供用于访问所有公共设备配置数据的接口。有关接口列表,请参见 libdevinfo(3LIB) 手册页。
以下摘录的 prtconf(1M) 命令示例显示了系统中的所有设备。
System Configuration: Sun Microsystems sun4u Memory size: 128 Megabytes System Peripherals (Software Nodes): SUNW,Ultra-5_10 packages (driver not attached) terminal-emulator (driver not attached) deblocker (driver not attached) obp-tftp (driver not attached) disk-label (driver not attached) SUNW,builtin-drivers (driver not attached) sun-keyboard (driver not attached) ufs-file-system (driver not attached) chosen (driver not attached) openprom (driver not attached) client-services (driver not attached) options, instance #0 aliases (driver not attached) memory (driver not attached) virtual-memory (driver not attached) pci, instance #0 pci, instance #0 ebus, instance #0 auxio (driver not attached) power, instance #0 SUNW,pll (driver not attached) se, instance #0 su, instance #0 su, instance #1 ecpp (driver not attached) fdthree, instance #0 eeprom (driver not attached) flashprom (driver not attached) SUNW,CS4231 (driver not attached) network, instance #0 SUNW,m64B (driver not attached) ide, instance #0 disk (driver not attached) cdrom (driver not attached) dad, instance #0 sd, instance #15 pci, instance #1 pci, instance #0 pci108e,1000 (driver not attached) SUNW,hme, instance #1 SUNW,isptwo, instance #0 sd (driver not attached) st (driver not attached) sd, instance #0 (driver not attached) sd, instance #1 (driver not attached) sd, instance #2 (driver not attached) ... SUNW,UltraSPARC-IIi (driver not attached) SUNW,ffb, instance #0 pseudo, instance #0
/devices 分层结构提供了表示设备树的名称空间。下面是 /devices 名称空间的缩写列表。样例输出对应于先前显示的示例设备树和 prtconf(1M) 输出。
/devices /devices/pseudo /devices/pci@1f,0:devctl /devices/SUNW,ffb@1e,0:ffb0 /devices/pci@1f,0 /devices/pci@1f,0/pci@1,1 /devices/pci@1f,0/pci@1,1/SUNW,m64B@2:m640 /devices/pci@1f,0/pci@1,1/ide@3:devctl /devices/pci@1f,0/pci@1,1/ide@3:scsi /devices/pci@1f,0/pci@1,1/ebus@1 /devices/pci@1f,0/pci@1,1/ebus@1/power@14,724000:power_button /devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:a /devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:b /devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:0,hdlc /devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:1,hdlc /devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:a,cu /devices/pci@1f,0/pci@1,1/ebus@1/se@14,400000:b,cu /devices/pci@1f,0/pci@1,1/ebus@1/ecpp@14,3043bc:ecpp0 /devices/pci@1f,0/pci@1,1/ebus@1/fdthree@14,3023f0:a /devices/pci@1f,0/pci@1,1/ebus@1/fdthree@14,3023f0:a,raw /devices/pci@1f,0/pci@1,1/ebus@1/SUNW,CS4231@14,200000:sound,audio /devices/pci@1f,0/pci@1,1/ebus@1/SUNW,CS4231@14,200000:sound,audioctl /devices/pci@1f,0/pci@1,1/ide@3 /devices/pci@1f,0/pci@1,1/ide@3/sd@2,0:a /devices/pci@1f,0/pci@1,1/ide@3/sd@2,0:a,raw /devices/pci@1f,0/pci@1,1/ide@3/dad@0,0:a /devices/pci@1f,0/pci@1,1/ide@3/dad@0,0:a,raw /devices/pci@1f,0/pci@1 /devices/pci@1f,0/pci@1/pci@2 /devices/pci@1f,0/pci@1/pci@2/SUNW,isptwo@4:devctl /devices/pci@1f,0/pci@1/pci@2/SUNW,isptwo@4:scsi
将驱动程序绑定到设备指的是系统选择用于管理特定设备的驱动程序的过程。绑定名称是将驱动程序与设备信息树连接在一起的唯一设备结点名称。对于设备树中的每个设备,系统都会尝试从已安装的驱动程序列表中选择一个驱动程序。
每个设备节点都有关联的 name 属性。可以在系统引导期间通过外部代理(如 PROM )或通过 driver.conf 配置文件指定此属性。无论在哪种情况下,name 属性都表示指定给设备树中的设备的节点名称。节点名称是在 /devices 中可见并列在 prtconf(1M) 输出中的名称。
设备节点也可以有关联的 compatible 属性。compatible 属性包含设备的一个或多个可能的驱动程序名称或驱动程序别名的有序列表。
系统使用 compatible 属性和 name 属性来为设备选择驱动程序。如果 compatible 属性存在,则系统会首先尝试将 compatible 属性的内容与系统中的驱动程序匹配。系统将从 compatible 属性列表的第一个驱动程序名称开始,尝试将该驱动程序名称与系统中的已知驱动程序匹配。系统将会处理该列表中的每一项,直到找到匹配项或者到达列表结尾。
如果 name 属性或 compatible 属性的内容与系统中的某个驱动程序匹配,则将该驱动程序绑定到设备节点。如果未找到匹配项,则不会将任何驱动程序绑定到设备节点。
某些设备将通用设备名称指定为 name 属性的值。通用设备名称用于描述设备的功能,不实际标识设备的特定驱动程序。例如,SCSI 主机总线适配器可能具有通用设备名称 scsi。以太网设备可能具有通用设备名称 ethernet。
通过 compatible 属性,系统可以确定具有通用设备名称的设备的备用驱动程序名称,例如,glm 对应于 scsi HBA 设备驱动程序,hme 对应于 ethernet 设备驱动程序。
具有通用设备名称的设备需要提供 compatible 属性。
有关通用设备名称的完整说明,请参见 IEEE 1275 Open Firmware Boot Standard。
下图显示了具有特定设备名称的设备节点。驱动程序绑定名称 SUNW,ffb 与设备节点名称同名。
下图显示了具有通用设备名称 display 的设备节点。驱动程序绑定名称 SUNW,ffb 是 compatible 属性驱动程序列表中与系统驱动程序列表中的驱动程序匹配的第一个名称。在这种情况下,display 是帧缓存器的通用设备名称。