Sysfs 是 Linux 设备驱动模型中至关重要的组成部分,它通过虚拟文件系统 /sys
向用户空间暴露内核对象(如设备、驱动、总线等)的属性和关系。作为驱动开发者,理解 Sysfs 的作用和实现机制,能够帮助你更好地调试设备、动态配置硬件,并与用户空间工具(如 udev
)交互。
1. Sysfs 的核心概念
- 虚拟文件系统:挂载在
/sys
目录下,以文件和目录的形式展示内核对象(kobject)的层次结构。 - 内核对象(kobject):Sysfs 的基础单元,每个 kobject 对应
/sys
中的一个目录,例如:/sys/bus
(总线)、/sys/devices
(设备)、/sys/class
(设备类)等。
- 属性(Attribute):文件的抽象,表示内核对象的某个属性(如设备名称、状态、配置参数等),支持读写操作。
2. Sysfs 与 kobject 的关系
Sysfs 的目录结构由内核对象(kobject)的层次关系决定:
- kobject 是内核中所有对象的基础结构(如
struct device
、struct device_driver
内部均包含 kobject)。 - 每个 kobject 在 Sysfs 中对应一个目录,其父 kobject 决定目录的位置。
- 示例:
/sys/devices/pci0000:00/0000:00:1c.0/0000:03:00.0/ # PCI 设备目录 ├── power/ # 电源管理属性 ├── vendor # 只读文件,设备厂商 ID └── uevent # 发送 uevent 事件的文件
3. 属性(Attribute)的实现
属性文件允许用户空间通过读写文件与内核交互。驱动开发者需要为设备或驱动定义属性,并实现其读写函数。
3.1 定义属性
使用 struct device_attribute
或宏 DEVICE_ATTR
定义属性:
// 定义属性结构体
static DEVICE_ATTR(foo, 0644, show_foo, store_foo);
// 实现读写函数
static ssize_t show_foo(struct device *dev,
struct device_attribute *attr,
char *buf) {
return sprintf(buf, "%d\n", device_foo_value(dev));
}
static ssize_t store_foo(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count) {
int value;
sscanf(buf, "%d", &value);
device_set_foo_value(dev, value);
return count;
}
3.2 创建/删除属性文件
在设备注册时创建属性文件,注销时删除:
// 在 probe 函数中创建属性
device_create_file(&my_device->dev, &dev_attr_foo);
// 在 remove 函数中删除属性
device_remove_file(&my_device->dev, &dev_attr_foo);
4. Sysfs 的典型应用场景
4.1 设备调试与配置
- 查看设备信息:
cat /sys/devices/pci0000:00/0000:00:1c.0/0000:03:00.0/vendor
- 动态修改参数:
echo 1 > /sys/class/gpio/gpio17/value # 控制 GPIO 电平
4.2 与 udev 交互
- uevent 事件:当设备状态变化时(如插入 USB 设备),内核通过 Sysfs 的
uevent
文件触发事件,用户空间的udev
根据规则创建设备节点(如/dev/sda
)。
5. 驱动开发中的 Sysfs 操作示例
5.1 定义一个 LED 控制属性
#include <linux/device.h>
static ssize_t led_show(struct device *dev,
struct device_attribute *attr,
char *buf) {
return sprintf(buf, "LED state: %s\n", led_get_state() ? "ON" : "OFF");
}
static ssize_t led_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count) {
int state;
if (sscanf(buf, "%d", &state) != 1)
return -EINVAL;
led_set_state(state);
return count;
}
// 定义属性
static DEVICE_ATTR(led, 0644, led_show, led_store);
// 注册属性(在 probe 函数中)
device_create_file(dev, &dev_attr_led);
// 用户空间操作示例:
// echo 1 > /sys/devices/.../my_device/led # 点亮 LED
// cat /sys/devices/.../my_device/led # 查看状态
5.2 通过 Sysfs 暴露设备统计信息...................................................
static ssize_t stats_show(struct device *dev,
struct device_attribute *attr,
char *buf) {
return sprintf(buf, "Interrupts: %u\nBytes Transferred: %lu\n",
dev->interrupt_count, dev->bytes_transferred);
}
static DEVICE_ATTR_RO(stats); // 只读属性宏
6. 注意事项
- 权限控制:属性文件的权限需合理设置(如
0644
表示用户可读写,其他用户只读)。 - 并发安全:确保读写函数(
show
/store
)是线程安全的,必要时使用锁。 - 避免过度暴露:仅暴露必要的属性,防止敏感信息泄漏。
- 内存管理:
show
函数中的sprintf
需确保缓冲区不溢出。
总结
Sysfs 是 Linux 驱动模型中连接内核与用户空间的关键桥梁,通过它开发者可以:
- 动态调试设备:查看硬件状态、配置参数。
- 实现用户空间控制:通过文件读写操作硬件(如 GPIO、传感器)。
- 与系统工具集成:
udev
依赖 Sysfs 自动创建设备节点。
深入理解 Sysfs 的实现和 API,能显著提升驱动开发的灵活性和调试效率。