2. 设备模型基础:Linux 设备模型核心概念及其注册流程

Linux 设备模型是内核管理硬件设备、驱动和总线拓扑的抽象框架。它为 ACPI、设备树等固件接口提供了统一的设备发现、电源管理和用户空间接口基础。理解 device、driver、bus、class 四个核心对象及其注册流程,是掌握设备匹配机制的前提。

核心要点:设备模型将物理或虚拟硬件抽象为内核对象,通过总线进行匹配与绑定,通过 class 向用户空间提供统一的设备访问接口。

2.1 四大核心对象

对象 内核结构体 职责 关键字段
device struct device 代表一个硬件设备或虚拟设备,包含资源、电源状态、拓扑关系 init_name, bus, driver, of_node, fwnode
driver struct device_driver 封装驱动逻辑,提供 probe/remove/shutdown 回调 name, bus, of_match_table, acpi_match_table
bus struct bus_type 管理设备和驱动的匹配、枚举、热插拔 name, match, probe, uevent
class struct class 按功能分类设备,提供 dev_attrs 和 uevent 接口 name, dev_uevent, devnode
调试提示:在 /sys/bus/、/sys/devices/、/sys/class/ 下可以实时查看这些对象的拓扑关系。例如 ls /sys/bus/platform/devices/ 列出所有 platform 设备。

2.2 对象关系与注册流程

设备模型的注册遵循“先总线、后设备与驱动”的顺序。核心流程如下:

  1. 总线注册:调用 bus_register(),创建 /sys/bus/<name> 目录,初始化 klist 链表。
  2. 设备注册:调用 device_register(),将设备挂入总线设备链表,触发总线 match 回调。
  3. 驱动注册:调用 driver_register(),将驱动挂入总线驱动链表,遍历总线设备链表进行匹配。
  4. 匹配与绑定:总线 match 回调返回真时,调用 driver_probe_device(),执行驱动的 probe 函数。
  5. class 注册:设备注册时可指定 class,在 /sys/class/ 下创建符号链接。
常见误区:设备注册时如果总线尚未注册,设备会进入 deferred 列表,等待总线出现后重新尝试匹配。调试时可通过 /sys/kernel/debug/devices_deferred 查看延迟设备。

2.3 注册流程 SVG 架构图

Linux 设备模型核心对象注册与匹配流程 设备模型核心对象注册与匹配流程 bus_register() device_register() driver_register() bus->match() driver->probe() class 接口创建 说明:设备与驱动注册顺序可互换,总线 match 回调在两者都就绪后触发 probe

2.4 关键数据结构与回调

总线 match 回调是设备与驱动绑定的核心决策点。以下为典型实现:

/* drivers/base/platform.c */
static int platform_match(struct device *dev, struct device_driver *drv)
{
    /* 1. 尝试 ACPI 匹配 */
    if (acpi_driver_match_device(dev, drv))
        return 1;

    /* 2. 尝试设备树 OF 匹配 */
    if (of_driver_match_device(dev, drv))
        return 1;

    /* 3. 尝试 ID table 匹配 */
    if (platform_match_id(drv->id_table, dev) != NULL)
        return 1;

    /* 4. 尝试名称直接匹配 */
    return strcmp(dev_name(dev), drv->name) == 0;
}
匹配优先级:ACPI → 设备树 → ID table → 名称。调试时可通过 echo 'match +p' > /sys/kernel/debug/dynamic_debug/control 开启总线匹配日志。

2.5 设备、驱动、总线注册核心 API

API 功能 注意事项
bus_register() 注册总线类型,创建 sysfs 目录 必须在设备/驱动注册前完成
device_register() 注册设备,加入总线设备链表 需提前设置 bus 和 parent
driver_register() 注册驱动,触发匹配与 probe 驱动可携带 of_match_table 或 acpi_match_table
class_create() 创建设备类,提供 uevent 接口 通常与 device_create() 配合使用

2.6 注册流程中的关键链表与 sysfs 路径

内核使用 klist 管理总线上设备和驱动的链表。注册后 sysfs 呈现如下结构:

/sys/bus/platform/
├── devices/          # 所有 platform 设备符号链接
│   ├── f0000000.uart -> ../../../devices/platform/f0000000.uart
│   └── ...
├── drivers/          # 所有 platform 驱动符号链接
│   ├── serial8250
│   └── ...
└── uevent            # 总线 uevent 属性

/sys/devices/platform/   # 设备实际目录,包含资源、电源、driver 链接
/sys/class/tty/          # class 分类目录,包含设备节点和属性
调试技巧:使用 udevadm info -a -p /sys/devices/platform/xxx 查看设备完整属性,包括 ACPI 句柄和设备树 compatible 字符串。

2.7 设备模型与固件接口的关联

设备模型通过 struct device 中的 fwnode 和 of_node 字段与 ACPI/设备树固件接口关联:

  • ACPI 设备:fwnode 指向 struct acpi_device,通过 acpi_match_device() 匹配 HID/CID。
  • 设备树设备:of_node 指向 struct device_node,通过 of_match_node() 匹配 compatible 字符串。
  • 统一设备属性:device_property_read_*() 系列 API 屏蔽底层差异,优先读取 ACPI 或设备树属性。