2. 基础知识

在 Linux 设备模型中,设备、驱动、总线、Class 与节点并不是彼此孤立的名词,而是一套围绕“发现—匹配—注册—暴露”构建起来的协作体系。理解它们之间的职责边界,是后续进行 Class 抽象、节点管理与工程化排障的基础。本节从核心原理出发,结合方法流程、典型场景与常见误区,建立一张可落地的概念地图。

一、核心原理:五个对象的分工与协作

Linux 设备模型的核心目标,是让内核以统一方式描述“系统里有什么硬件、由谁驱动、通过什么路径连接、对外呈现为什么能力”。围绕这一目标,形成了五个关键角色:

  • 设备(device):对具体硬件实例的抽象,例如一颗 I2C 温度传感器、一块 PCIe 网卡、一个 GPIO 按键。它描述“存在什么”,通常带有资源信息(地址、中断号、时钟、GPIO 等)。
  • 驱动(driver):对某类设备的操作逻辑封装,负责初始化、读写、电源管理、中断处理等。它描述“怎么操作”,并通过匹配机制绑定到设备。
  • 总线(bus):设备与驱动之间的“撮合者”,如 platform、I2C、SPI、PCI、USB。总线负责枚举/注册设备、维护驱动链表、执行匹配规则,并在匹配成功后触发 probe。
  • Class(类):从“功能视角”对设备进行归类,如 input、net、tty、block、leds、hwmon。Class 不关心物理连接方式,而关心“对外提供什么能力”,并据此生成统一的用户空间接口。
  • 节点(node):用户空间可见的入口,通常表现为 /dev 下的设备节点、/sys 下的属性文件,或 /proc 中的信息节点。节点是应用与内核交互的“门把手”。

它们的关系可以概括为:总线负责匹配,驱动负责实现,设备负责实例,Class 负责归类,节点负责暴露。一个设备可以同时属于某条总线和某个 Class;一个驱动可以服务多个同类设备;Class 则把分散在不同总线上的同类能力汇聚成一致的用户接口。

Linux 设备模型:对象、匹配与暴露 设备 device 硬件实例 + 资源 如:i2c-1 上的传感器 驱动 driver 操作逻辑 + probe 如:tmp102 驱动 总线 bus 匹配 + 枚举 platform / i2c / spi 注册 匹配 Class 类 功能归类:input / net / tty 节点 node /dev /sys /proc 接口 按能力归类 生成用户接口 设备树 / ACPI 描述硬件拓扑 提供实例 sysfs / devtmpfs 节点与属性载体 呈现 用户空间应用 open / read / write / ioctl 访问 udev / mdev 动态创建 /dev 节点 触发

二、方法流程:从硬件到节点的注册链路

以一颗挂在 I2C 总线上的温度传感器为例,典型链路如下:

  1. 设备树或 ACPI 描述该传感器挂在哪条 I2C 控制器下、地址是多少、使用哪个中断。
  2. I2C 控制器驱动注册 i2c_adapter,内核据此创建 i2c 总线上的设备实例。
  3. 传感器驱动通过 i2c_driver 注册到 i2c 总线,声明 of_match_table 或 id_table。
  4. 总线执行匹配,匹配成功后调用驱动的 probe 函数,完成寄存器初始化与子系统注册。
  5. 驱动向 hwmon 或 iio 等 Class 注册,Class 在 /sys/class/hwmon 下生成统一属性。
  6. 若需要字符设备接口,驱动申请设备号并创建 cdev,udev 依据 sysfs 信息在 /dev 下生成节点。

这条链路体现了“总线匹配—驱动 probe—Class 归类—节点暴露”的标准顺序。任何一环缺失,用户空间都无法稳定拿到可用接口。

三、典型应用场景与术语对照

不同总线与 Class 的组合,对应着不同的工程场景。下表给出常见术语与典型用途的对照,便于快速定位问题域。

术语 所属层次 典型对象 用户空间表现
platform_device 总线(platform) SoC 内部 GPIO、UART、I2C 控制器 /sys/bus/platform/devices/
i2c_client 总线(i2c) 温度传感器、EEPROM、PMIC /sys/bus/i2c/devices/1-0048
input_dev Class(input) 按键、触摸屏、鼠标 /dev/input/eventX
net_device Class(net) 以太网、Wi-Fi、CAN ip link 可见的网口
cdev 字符设备 自定义字符驱动 /dev/xxx
sysfs attribute 节点属性 亮度、采样率、使能开关 /sys/class/.../xxx

四、案例与排障:节点缺失时如何定位

假设应用反馈“/dev/tmp102 不存在”。按设备模型链路逐层排查,通常比盲目改驱动更高效:

# 1. 确认设备是否被总线枚举
ls /sys/bus/i2c/devices/
# 期望看到 1-0048 之类的目录

# 2. 确认驱动是否已注册并匹配
ls /sys/bus/i2c/drivers/tmp102/
# 若目录下没有 1-0048 软链接,说明未匹配

# 3. 查看 probe 是否成功
dmesg | grep -i tmp102
# 关注 probe success / failed 与错误码

# 4. 确认 Class 是否注册
ls /sys/class/hwmon/
# 找到对应 hwmonN,读取 name 属性确认

# 5. 确认 udev 是否生成节点
udevadm info -q path -n /dev/tmp102
udevadm monitor --udev
实用技巧:在 sysfs 中,设备目录通常包含 driver 软链接、subsystem 软链接与 uevent 文件。通过 readlink 追踪这些链接,可以快速判断设备属于哪条总线、绑定了哪个驱动、归入哪个 Class。

五、常见误区与复盘

  • 把 Class 当总线用。Class 只做功能归类,不负责匹配。设备匹配永远发生在总线上,Class 只是把已匹配设备的能力统一暴露。
  • 认为有设备节点就等于驱动正常。节点可能由 udev 依据残留规则创建,真正是否可用要看 probe 是否成功、Class 属性是否可读。
  • 忽略设备树与总线的对应关系。设备树节点必须挂在正确的总线控制器下,否则内核不会创建对应总线设备,驱动自然无法匹配。
  • 混淆主次设备号与节点名。节点名由 udev 规则决定,主次设备号由驱动分配;改名不影响内核访问,但会影响应用路径。
  • 排障只看应用层。应从“总线枚举—驱动匹配—probe—Class 注册—节点生成”逐层验证,避免在错误层次反复修改。