Linux 内核设备模型

Patrick Mochel <mochel@digitalimplant.org>

草拟于 2002 年 8 月 26 日,更新于 2006 年 1 月 31 日

概述

Linux 内核驱动程序模型是对先前内核中使用的所有不同的驱动程序模型的统一。它的目的是通过将一组数据和操作整合到全局可访问的数据结构中来增强桥接器和设备的特定于总线的驱动程序。

传统的驱动程序模型为它们控制的设备实现某种树状结构(有时只是一个列表)。不同总线类型之间没有任何统一性。

当前的驱动程序模型提供了一个通用的、统一的数据模型,用于描述总线以及可以出现在该总线下的设备。统一的总线模型包括所有总线都携带的一组通用属性,以及一组通用回调,例如总线探测期间的设备发现、总线关闭、总线电源管理等。

通用的设备和桥接器接口反映了现代计算机的目标:即实现无缝的设备“即插即用”、电源管理和热插拔。特别是,英特尔和微软(即 ACPI)规定的模型确保了几乎在任何 x86 兼容系统上的任何总线上的几乎每个设备都可以在此范例中工作。当然,并非每个总线都能够支持所有此类操作,尽管大多数总线都支持大多数此类操作。

下游访问

通用数据字段已从各个总线层移动到通用数据结构中。这些字段仍然必须由总线层访问,有时由特定于设备的驱动程序访问。

鼓励其他总线层执行为 PCI 层所做的事情。 `struct pci_dev` 现在看起来像这样

struct pci_dev {
      ...

      struct device dev;     /* Generic device interface */
      ...
};

首先请注意,`struct pci_dev` 中的 struct device dev 是静态分配的。这意味着设备发现时只有一个分配。

还要注意,struct device dev 不一定在 pci_dev 结构的前面定义。 这是为了让人在总线驱动程序和全局驱动程序之间切换时考虑他们在做什么,并阻止两者之间无意义和不正确的强制转换。

PCI 总线层可以自由访问 struct device 的字段。 它知道 `struct pci_dev` 的结构,并且应该知道 struct device 的结构。已转换为当前驱动程序模型的各个 PCI 设备驱动程序通常不应该接触 struct device 的字段,除非有令人信服的理由这样做。

上述抽象避免了过渡阶段不必要的痛苦。 如果不是这样完成的,那么当一个字段被重命名或删除时,每个下游驱动程序都会中断。 另一方面,如果只有总线层(而不是设备层)访问 struct device,那么只需要总线层进行更改。

用户界面

由于可以完整地分层查看系统中的所有设备,因此向用户空间导出完整的分层视图变得相对容易。 这可以通过实现一个名为 sysfs 的专用虚拟文件系统来实现。

几乎所有主流 Linux 发行版都会自动挂载此文件系统;您可以在“mount”命令的输出中看到以下的一些变体

$ mount
...
none on /sys type sysfs (rw,noexec,nosuid,nodev)
...
$

通常,sysfs 的自动挂载是通过 /etc/fstab 文件中类似以下的条目来完成的

none          /sys    sysfs    defaults               0 0

或者在基于 Debian 的系统的 /lib/init/fstab 文件中有类似的内容

none            /sys    sysfs    nodev,noexec,nosuid    0 0

如果 sysfs 没有自动挂载,您始终可以使用以下命令手动挂载它

# mount -t sysfs sysfs /sys

每当设备插入树中时,都会为其创建一个目录。此目录可以在发现的每一层填充 - 全局层、总线层或设备层。

全局层当前创建两个文件 - ‘name’ 和 ‘power’。前者仅报告设备的名称。后者报告设备的当前电源状态。它也将用于设置当前电源状态。

总线层还可以为其在探测总线时找到的设备创建文件。例如,PCI 层当前为每个 PCI 设备创建“irq”和“resource”文件。

特定于设备的驱动程序也可以在其目录中导出文件,以公开特定于设备的数据或可调接口。

有关 sysfs 目录布局的更多信息,请参见此目录中的其他文档以及文件 sysfs - 用于导出内核对象的 _The_ 文件系统