3. sysfs与设备模型

在Linux用户空间设备管理中,sysfs是内核设备模型向用户空间暴露的“结构化视图”。它通常挂载在/sys,以目录和文件的形式呈现设备、驱动、总线及其相互关系。与/dev下的设备节点不同,/sys不用于读写数据流,而是用于观察拓扑、查询属性、触发匹配、调试绑定。理解sysfs,核心是理解内核设备模型中的几个基本对象:kobject、kset、device、driver、bus,以及它们如何被组织成可遍历的树。

一、核心原理:从kobject到设备模型

内核设备模型的最小基石是kobject。它本身不直接面向用户空间,而是提供引用计数、父子层级、sysfs目录项等基础能力。多个kobject可以组成kset,kset是一种容器,用于把同类kobject聚合在一起,并形成层级。用户空间看到的每一个/sys目录,通常对应一个kobject;目录中的属性文件,则对应kobject的attribute。

在此之上,内核抽象出三类关键对象:

  • bus:总线类型,如pci、usb、platform,负责设备与驱动的匹配规则。
  • device:具体设备实例,描述“有什么硬件”。
  • driver:设备驱动,描述“如何操作硬件”。

bus、device、driver三者通过sysfs中的符号链接和目录形成关系网。设备注册到总线后,总线负责寻找匹配驱动;驱动注册到总线后,总线负责寻找匹配设备。匹配成功后,双方在sysfs中建立绑定链接。因此,/sys不仅是信息展示,更是设备-驱动匹配状态的实时映射。

Linux 设备模型核心对象关系与 /sys 映射 kobject 引用计数 · 父子层级 · sysfs目录 kset 同类kobject容器 · 聚合层级 用户空间可见形式 /sys 目录 + 属性文件 + 符号链接 bus(总线) /sys/bus/pci · usb · platform device(设备) /sys/devices/... · 具体硬件实例 driver(驱动) /sys/bus/*/drivers/... · 操作逻辑 总线匹配 device ↔ driver /sys 典型目录结构(简化) /sys/ ├── bus/ → 总线类型,含 devices/ 与 drivers/ 子目录 ├── devices/ → 全局设备树,按物理拓扑组织 ├── class/ → 按功能分类(net、tty、block 等) └── module/ → 已加载内核模块信息 图例 kobject / kset bus device driver

二、/sys目录结构与遍历方法

/sys下最常用的顶层目录包括:

目录 含义 典型用途
/sys/bus/ 按总线类型组织 查看某总线下所有设备和驱动
/sys/devices/ 全局设备树 按物理拓扑遍历设备层级
/sys/class/ 按功能分类 快速定位网卡、块设备、tty等
/sys/block/ 块设备视图 查看磁盘及分区
/sys/module/ 内核模块信息 查看模块参数与引用计数

遍历sysfs时,最有效的方式是从bus出发,沿devices和drivers两个方向展开。例如查看PCI总线上的设备与驱动绑定情况:

# 列出PCI总线下的所有设备
ls /sys/bus/pci/devices/

# 列出PCI总线下的所有驱动
ls /sys/bus/pci/drivers/

# 查看某设备绑定的驱动(符号链接)
ls -l /sys/bus/pci/devices/0000:00:1f.2/driver

# 查看某驱动绑定的所有设备
ls /sys/bus/pci/drivers/ahci/

另一个重要入口是/sys/devices,它反映的是设备的物理拓扑。例如从PCI根桥逐级向下,可以看到桥、控制器、终端设备。使用find或ls -R可以递归遍历,但更推荐结合readlink查看符号链接,理解设备与驱动、设备与类之间的多对多关系。

三、案例:定位一块网卡的完整信息链

假设需要排查eth0对应的硬件、驱动和总线信息,可以按以下路径遍历:

# 1. 从class找到设备
ls -l /sys/class/net/eth0
# 输出类似:eth0 -> ../../devices/pci0000:00/0000:00:1c.0/0000:02:00.0/net/eth0

# 2. 进入设备目录查看属性
cd /sys/class/net/eth0
cat address          # MAC地址
cat speed            # 链路速率
cat operstate        # 运行状态

# 3. 查看绑定的驱动
ls -l /sys/class/net/eth0/device/driver
# 输出类似:driver -> ../../../../bus/pci/drivers/e1000e

# 4. 查看所属总线
ls /sys/class/net/eth0/device/subsystem
# 输出类似:subsystem -> ../../../../bus/pci

这条链路清晰展示了:class入口 → device实例 → driver绑定 → bus归属。在udev规则编写中,这种遍历能力非常关键,因为udev规则常需要根据SUBSYSTEM、DRIVER、ATTR{}等条件匹配设备。

四、误区与复盘

常见误区一:把/sys当作/dev使用。 /sys中的属性文件通常用于查询和配置,不是数据读写通道。例如/sys/class/net/eth0/address只读,不能用来发送网络数据。

常见误区二:忽略符号链接。 sysfs中大量关系通过符号链接表达,如driver、subsystem、device。直接ls看不到真实路径,应使用ls -l或readlink -f。

常见误区三:认为目录层级等于物理层级。 /sys/class是按功能分类的视图,/sys/devices才是物理拓扑。两者通过符号链接关联,不能混为一谈。

遍历建议:排查设备问题时,优先使用/sys/bus/<bus>/devices/和/sys/bus/<bus>/drivers/交叉比对;编写udev规则时,优先使用udevadm info -a -p /sys/class/...自动提取属性链,避免手工拼路径。

掌握sysfs的设备模型,本质上是掌握一种“内核视角的设备关系语言”。kobject和kset提供底层骨架,bus、device、driver构成匹配与绑定的三角关系,而/sys则是这套关系在用户空间的可读投影。能够熟练遍历/sys,就能在设备管理、驱动调试和udev规则编写中快速定位问题根因。