08、Sysfs设备驱动管理 *
📢Linux
系统中一切皆文件。
一、什么是sysfs设备驱动管理
sysfs是Linux系统中的一个虚拟文件系统,它把内核里的设备、驱动等信息以文件和文件夹的形式展示出来。用户通过访问/sys目录下的文件,就能查看或调整硬件和驱动的设置。
具体来说:
- 它像一个"控制面板",把内核里的各种对象(设备、驱动等)信息整理成树状文件结构
- 所有内容都放在系统的/sys文件夹里
- 用户可以直接读取文件查看硬件状态,或修改文件来调整设备参数,就像操作普通文本文件一样简单
这种设计让用户能直观地通过文件系统接口,方便地管理和配置底层硬件设备。
二、虚拟文件系统 sysfs 目录层次
我们进入到 Linux 系统的/sys 目录下, 可以看到如下文件夹
和设备模型有关的文件夹为 bus, class, devices。 完整路径为如下所示:
- /sys/bus
- /sys/class
- /sys/devices
/sys/devices: 该目录包含了系统中所有设备的子目录。 每个设备子目录代表一个具体的设备, 通过其路径层次结构和符号链接反映设备的关系和拓扑结构。 每个设备子目录中包含了设备的属性、 状态和其他相关信息。 如下所示:
/sys/bus: 该目录包含了总线类型的子目录。 每个子目录代表一个特定类型的总线, 例如PCI、 USB 等。 每个总线子目录中包含与该总线相关的设备和驱动程序的信息。
比如 I2C 总线下连接的设备, 如下所示 :
/sys/class: 该目录包含了设备类别的子目录。 每个子目录代表一个设备类别, 例如磁盘、网络接口等。 每个设备类别子目录中包含了属于该类别的设备的信息。 如下图所示:
- 清晰分类 把设备按类型分门别类,就像把同类物品放在一个抽屉里。这样找东西时,同类设备都集中在一起,管理起来更方便。
- 统一规则 同一类设备能用统一的方法和属性来操作。比如所有打印机都有"打印"功能,这样写程序或驱动时,不用为每个设备重新定义基础规则,省时省力。
- 快速查找 找设备时可以直接到对应分类里搜索,比如直接找"摄像头"类,不用翻遍整个系统。这就像在超市直接去零食区找薯片,效率更高。
- 轻松扩展 新设备来了?只要它属于现有分类(比如新键盘属于"输入设备"),就能直接加入,不需要改系统底层或原有程序。就像新书放进已有的书架格子,不用重搭书架。这样系统更灵活,开发和升级也更轻松。
比如应用现在要设置 gpio
如果使用类可以直接使用以下命令:
echo 1 > /sys/class/gpio/gpio157/value
如果不使用类, 使用以下命令:
echo 1 > /sys/devices/platform/fe770000.gpio/gpiochip4/gpio/gpio157/value
三、虚拟文件系统 sysfs 目录层次
当你使用kobject时,一般不会单独使用它,而是把它作为其他结构的一部分来用。
这样做是为了把具体的设备类型添加到设备管理系统里。例如,字符设备使用的cdev结构体和平台设备使用的platform_device结构体,就像下面这样:
struct cdev {
struct kobject kobj;//内嵌到 cdev 中的 kobject
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
}
2
3
4
5
6
7
8
struct platform_device {
const char*name;
int id;
bool id_auto;
struct device dev;
u32 num_resources;
struct resource *resource;
const struct platform_device_id *id_entry;
char *driver_override; /* Driver name to force a match */
/* MFD cell pointer */
struct mfd_cell *mfd_cell;
/* arch specific additions */
struct pdev_archdata archdata;
};
2
3
4
5
6
7
8
9
10
11
12
13
14
device 结构体如下所示:
struct device {
//设备的父设备
struct device *parent;
//私有指针
struct device_private *p;
//对应的 kobj
struct kobject kobj;
//设备初始化的名字
const char *init_name;
//设备类型
const struct device_type *type;
//设备所属的总线
struct bus_type *bus;
struct device_driver *driver;
...........
//设备所属的类
struct class *class;
//设备的属性组
const struct attribute_group **groups; /* optional groups */
...........
};
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
总线、设备和驱动都可以看作是kobject的子类。因为它们都是设备模型中的组成部分,通过继承kobject来实现与设备模型的整合。
可以说kobject是设备模型的核心基础。它通过创建对应的系统目录和属性文件,为管理设备模型中的各个组件提供了统一的系统和操作方式。
四、Sysfs设备驱动管理相关注册接口
在Linux系统中,设备文件主要分布在两个目录:/dev
和/sys
,它们的作用完全不同:
① /dev
目录
这是实际操作设备的入口。
作用:存放所有硬件设备的“门牌号”(设备文件),比如硬盘(
/dev/sda
)、键盘(/dev/input
)等。特点:
- 这些文件是“真实的”,但实际内容为空,只是个标识符。
- 由系统(如
udev
工具)自动生成,或通过mknod
命令手动创建。 - 程序通过
open
、read/write
等操作这些文件时,会直接与硬件交互(背后由驱动程序实现)。
② /sys
目录
这是内核的“信息公示栏”。
作用:内核把设备、驱动、总线等的内部状态和关系,以文件形式“翻译”出来,方便用户查看或调试。
特点:
- 不是操作设备的入口,而是展示设备的元信息(如型号、状态、参数等)。
- 由内核自动生成,内容动态变化,随设备增减而更新。
- 结构清晰,能直接看到设备与驱动、总线之间的归属关系(比如哪个驱动管理哪个设备)。
③ 关键区别
用途 | /dev | /sys |
---|---|---|
做什么 | 操作设备的“接口” | 展示设备的“说明书” |
内容 | 真实设备文件 | 内核导出的元数据 |
如何生成 | 系统自动生成(udev) | 内核运行时生成 |
用户怎么用 | 读写设备(如cat /dev/sda ) | 查看/调试(如cat /sys/class/power_supply/battery/capacity ) |
4.1、device_register都做了些什么?
设备在初始化时会在/sys/devices目录下创建自己的专属目录(比如xxx-device),并在里面存放属性文件(记录设备信息)。同时,设备会创建一个名为subsystem的特殊链接,指向总线(bus)或类别(class)目录:
- 归属关系 如果链接指向总线目录(如USB、PCI),说明设备由该总线管理;如果指向类别目录(如输入设备、网络设备),则表示设备属于这类功能设备,虽然看起来是分类,但本质是设备具有共同特性或统一管理方式。
- 避免重复 在总线或类别的主目录(/sys/bus或/sys/class)下,不需要重复存放设备信息,只需创建一个指向设备目录的快捷链接。这样通过总线或类别的目录,就能直接看到所有归属设备。
- 数据结构的比喻 整个系统用文件和目录模拟数据结构:
- 目录 = 结构体(存储设备信息的容器)
- 文件 = 数据成员(记录具体参数)
- 链接 = 指针(连接不同结构体之间的关系)
通过这种设计,设备、总线、类别之间的关联关系就像程序中的数据结构一样被清晰组织。
4.2、driver_register都做了些什么?
driver_register
函数在Linux内核驱动模型中承担着关键的注册机制,其核心职责是将驱动对象(driver object)无缝集成到内核设备模型框架中。相较于设备注册流程,其操作更为聚焦于驱动与总线子系统、sysfs文件系统以及模块管理子系统的交互。具体实现机制包含以下关键步骤:
- sysfs节点创建与注册 通过调用
driver_add()
函数,驱动对象在对应的总线类设备目录(如/sys/bus/pci/drivers
或/sys/bus/platform/drivers
)下创建与驱动名称强关联的虚拟目录节点。该目录结构遵循内核设备模型的层次化组织规范,为后续的驱动属性暴露和设备匹配提供基础。 - 驱动属性接口建立 在驱动的sysfs目录下,通过
driver_create_file()
接口注册标准化的驱动属性文件节点(如uevent
、id
等),同时支持驱动开发者通过DRIVER_ATTR()
宏定义扩展自定义属性接口。这些属性接口遵循sysfs的文件操作接口规范,为用户空间工具提供驱动状态查询和配置的标准化通道。 - 模块关联机制实现 通过sysfs的符号链接机制建立驱动与所属内核模块的双向关联:在驱动目录下创建指向模块sysfs节点(
/sys/module/<module_name>
)的符号链接module
,同时在模块目录下创建指向驱动节点的drivers
链接。这种基于kobject对象的双向引用关系,确保了驱动与模块的生命周期管理一致性。 - 总线匹配注册 驱动注册过程最终通过
bus_add_driver()
完成与目标总线子系统的绑定,将驱动对象注册到总线的驱动链表中。该过程会触发总线层的驱动与设备匹配机制(通过bus_for_each_dev()
遍历总线设备列表),实现驱动与已有设备的自动绑定,并通过driver_bound()
回调完成设备初始化。
该注册流程实质上构建了驱动对象在内核设备模型中的完整拓扑位置,同时实现了驱动能力声明、属性暴露、模块关联及设备匹配四大核心功能。这种分层设计确保了驱动框架的可扩展性,使得新总线类型和驱动模型能够通过继承和扩展现有机制实现无缝集成。
4.3、bus_register都做了些什么?
在Linux内核中,当注册一个新的总线(bus)时,系统会在/sys/bus
目录下创建一个以总线名称命名的文件夹(比如叫xxx-bus
)。这个总线目录里会自动包含两个子目录:
- devices:用来存放这个总线下所有设备的信息;
- drivers:用来存放管理这些设备的驱动程序。
同时,这个总线目录还会生成两个特殊文件:
drivers_probe
和drivers_autoprobe
:用户可以通过修改这两个文件,手动触发驱动的检测和自动加载;uevent
:用于监听和响应设备的事件变化(比如设备插入或移除)。
此外,总线还能添加自己特有的配置或状态参数文件。