一、sysfs到底是什么?
sysfs是 Linux 内核用于导出“设备对象模型”信息的一种可视化接口。
其本质是一个基于kobject机制、面向设备模型(device model),用于展示内核数据结构、并允许读写设备属性的虚拟文件系统。它的固定挂载点为 /sys。通过操作这个目录下的文件,用户空间程序可以与内核交互,查询或配置设备状态,这是Linux系统编程与驱动开发中的重要组成部分。
二、基于device创建sysfs节点
2.1 定义device_attribute
创建sysfs节点的核心是定义device_attribute结构。内核提供了便捷的宏DEVICE_ATTR:
#define DEVICE_ATTR(_name, _mode, _show, _store) \
struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)
__ATTR宏在include/linux/sysfs.h中定义,它将参数组装成一个标准的attribute结构:
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
最终,struct device_attribute结构体(定义于include/linux/device.h)将attribute与读写函数绑定:
/* interface for exporting device attributes */
struct device_attribute {
struct attribute attr;
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
};
其中的struct attribute(亦定义于include/linux/sysfs.h)则定义了属性节点的基本元信息:
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};
2.2 定义属性读写函数
.show和.store成员是两个函数指针,需要驱动开发者实现。
show函数用于响应cat命令,向用户空间返回信息:
ssize_t (*show)(struct device *dev, struct device_attribute *attr,
char *buf);
//入参buf是内核提供的缓冲区,用于填充要显示的内容。
//返回值应为填充的字节数,且总长度应小于PAGE_SIZE(通常4096字节)。
//dev和attr参数可用于获取特定设备或属性的上下文。
store函数用于响应echo命令,处理从用户空间传入的数据:
ssize_t (*store)(struct device *dev, struct device_attribute *attr,
const char *buf, size_t count);
//入参buf是用户传入的字符串数据。
//入参count是buf中数据的长度。
//函数通常应返回成功处理的字节数(常直接返回count)。
理解并正确实现这两个回调函数是Linux内核驱动与用户空间进行数据交换的关键。
三、将device attribute添加到sysfs中
定义好属性结构后,需要将其与内核对象(kobject)关联,从而在/sys下创建出可见的文件节点。
3.1 创建单个节点:sysfs_create_file
对于单个属性节点,通常在驱动的probe函数或module_init中调用sysfs_create_file,在退出时调用sysfs_remove_file进行清理。
int __must_check sysfs_create_file(struct kobject *kobj, const struct attribute *attr);
void sysfs_remove_file(struct kobject *kobj, const struct attribute *attr);
这里的kobj通常是&dev->kobj(设备对象的kobject)。
3.2 创建多个节点(推荐):sysfs_create_group
当需要创建多个相关属性时,使用属性组(attribute group)一次注册更为高效和整洁。
首先,定义多个DEVICE_ATTR,并将其指针放入一个数组中(数组必须以NULL结尾):
static DEVICE_ATTR(sys_device_file, S_IWUSR | S_IRUSR, show_sys_device, store_sys_device);
static DEVICE_ATTR(sys_device_file0, S_IWUSR | S_IRUSR, show_sys_device, store_sys_device);
static DEVICE_ATTR(sys_device_file1, S_IWUSR | S_IRUSR, show_sys_device, store_sys_device);
static DEVICE_ATTR(sys_device_file2, S_IWUSR | S_IRUSR, show_sys_device, store_sys_device);
static struct attribute *sys_device_attributes[] = {
&dev_attr_sys_device_file.attr,
&dev_attr_sys_device_file0.attr,
&dev_attr_sys_device_file1.attr,
&dev_attr_sys_device_file2.attr,
NULL // 属性结构体数组最后一项必须以NULL结尾。
};
然后,将这些属性数组包装成一个attribute_group:
static const struct attribute_group sys_device_attr_group = {
.attrs = sys_device_attributes,
};
最后,使用sysfs_create_group一次性注册整个组,使用sysfs_remove_group进行移除:
int sysfs_create_group(struct kobject *kobj, const struct attribute_group *grp)
void sysfs_remove_group(struct kobject *kobj, const struct attribute_group *grp)
这种方法逻辑清晰,便于管理,是Linux系统驱动开发中的标准实践。