2. Linux设备模型基础

Linux 设备模型是内核为统一管理总线、设备与驱动而抽象出的一套对象体系。它把“谁挂在谁下面”“谁由谁驱动”“谁向用户暴露什么属性”这些关系,用 kobject、kset、device、driver、bus 等结构串成一张有层次、可遍历、可导出的图。理解这张图,是理解热插拔与自动发现机制的前提:uevent 从哪里来、sysfs 目录为什么长成那样、驱动何时与设备绑定,答案都在这套模型里。

一、核心对象与层次关系

设备模型的最小引用计数单元是 kobject,它本身不表达业务含义,只负责名字、引用计数、父对象和 sysfs 目录项。多个 kobject 通过 kset 组织成集合,kset 既是容器,也常作为某一类对象的顶层目录。在此之上,device 表示一个具体设备,driver 表示能驱动某类设备的软件实体,bus 表示连接设备与驱动的匹配规则和枚举通道。它们的关系可以概括为:bus 负责匹配,device 与 driver 在匹配成功后绑定,kset 与 kobject 负责把它们组织成树并导出到 sysfs。

Linux 设备模型核心对象与 sysfs 导出关系 kobject 引用计数 / 名字 / 父对象 kset 同类 kobject 集合 sysfs /sys 目录树 bus(总线) match / probe / uevent device(设备) struct device kobject + parent + bus driver(驱动) struct device_driver probe / remove / id_table 匹配成功后绑定 用户空间可见接口(sysfs 属性文件) /sys/bus/…/devices/… /sys/devices/… /sys/class/… /sys/dev/… uevent modalias driver subsystem 读写属性 → 触发内核回调 → 影响设备状态或驱动行为

二、sysfs:设备模型在用户空间的投影

sysfs 通常挂载在 /sys,它不是普通文件系统,而是设备模型对象树的“视图”。每个 kobject 对应一个目录,每个属性对应一个文件。用户空间通过读写这些文件,就能查询设备状态、修改驱动参数,甚至触发设备重新探测。常见入口包括:

  • /sys/devices/:按物理拓扑组织的设备树,是设备模型的真实层次。
  • /sys/bus/<bus>/devices/ 与 /sys/bus/<bus>/drivers/:按总线视角查看设备与驱动,以及它们的绑定关系。
  • /sys/class/<class>/:按功能分类的视图,如 net、tty、block,方便用户空间按用途查找。
  • /sys/dev/char/ 与 /sys/dev/block/:由设备号反查设备对象。

其中 uevent 文件尤其关键:向它写入 add、remove、change 等字符串,内核会重新向用户空间发送 uevent,udev/mdev 等守护进程据此创建设备节点或执行规则。这正是热插拔自动发现链路中“内核通知用户空间”的出口。

三、bus、device、driver 的匹配与绑定流程

设备与驱动的结合不是自动发生的,而是由 bus 的匹配逻辑驱动。以 platform 总线为例,流程可以概括为:设备注册到 bus 的设备链表,驱动注册到 bus 的驱动链表,bus 对每一对 device/driver 调用 match 回调,匹配成功后调用 driver 的 probe。probe 成功,sysfs 中就会出现 driver 符号链接,表示绑定完成。

# 查看 platform 总线下的设备与驱动
ls /sys/bus/platform/devices/
ls /sys/bus/platform/drivers/

# 查看某设备当前绑定的驱动
ls -l /sys/bus/platform/devices/ff0a0000.serial/driver

# 手动触发驱动绑定与解绑
echo ff0a0000.serial > /sys/bus/platform/drivers/serial8250/bind
echo ff0a0000.serial > /sys/bus/platform/drivers/serial8250/unbind

# 查看设备 uevent 内容
cat /sys/bus/platform/devices/ff0a0000.serial/uevent

匹配依据通常来自设备树 compatible、ACPI ID、PCI vendor/device ID 或 platform_device 的 name。驱动侧通过 id_table 或 of_device_id 声明自己能处理哪些设备。匹配失败时,设备会停留在“未绑定”状态,sysfs 中不会出现 driver 链接,用户空间也就无法通过该驱动访问设备。

四、用户空间可见接口与典型操作

sysfs 属性大致分为只读状态、可写控制、以及符号链接三类。只读属性用于观测,如 uevent、modalias、dev;可写属性用于控制,如 bind、unbind、state;符号链接用于表达关系,如 driver、subsystem、device。下表列出常见接口及其用途。

接口路径 类型 用途
/sys/bus/<bus>/devices/<dev>/uevent 只读/可写 查看或触发设备 uevent,热插拔通知入口
/sys/bus/<bus>/devices/<dev>/driver 符号链接 指向当前绑定的驱动目录
/sys/bus/<bus>/drivers/<drv>/bind 可写 手动将设备绑定到该驱动
/sys/bus/<bus>/drivers/<drv>/unbind 可写 手动解绑设备与驱动
/sys/class/<class>/<dev>/dev 只读 查看主次设备号,供 mknod 使用
/sys/devices/.../modalias 只读 查看设备匹配别名,用于驱动自动加载
调试热插拔问题时,优先查看 /sys/bus/<bus>/devices/<dev>/uevent 与 driver 链接是否存在。前者说明内核是否已识别设备,后者说明驱动是否已绑定成功。

五、常见误区与复盘

第一个误区是把 sysfs 当成普通配置文件系统。sysfs 的属性读写会直接进入内核回调,写错值可能触发设备复位或驱动异常,因此生产环境应避免随意写 bind/unbind。第二个误区是认为“设备出现在 /sys 下就等于驱动已加载”。实际上设备对象可以先注册,驱动后匹配;只有 probe 成功,driver 链接才会出现。第三个误区是混淆 /sys/devices 与 /sys/bus:前者是物理拓扑树,后者是总线视角的索引,两者指向同一批 kobject,但遍历路径不同。

复盘时可以用一条命令串起整条链路:先看设备是否注册,再看 uevent 是否可读,再看 driver 链接是否存在,最后看 dmesg 中 probe 是否成功。这样就能把“设备模型—sysfs—uevent—驱动绑定”串成一条可验证的排查路径,为后续热插拔与自动发现机制打下基础。