一、sysfs简介
sysfs 是Linux内核提供的一种虚拟文件系统,挂载在 /sys 目录下,用于统一管理设备、驱动和内核模块的层次化信息。它是Linux设备模型的核心组成部分。
二、sysfs的作用
-
暴露设备和驱动信息
用户可以通过 /sys 查看硬件设备(如 CPU、USB、PCI 设备)、内核模块、电源管理等数据。 -
动态设备管理
支持热插拔(如 U 盘插入时自动在 /sys 和 /dev 生成节点)。 -
内核与用户空间交互
允许通过读写 /sys 下的文件调整内核参数(如调节屏幕亮度、CPU 频率)。
三、sys目录结构
/sys 按类别组织为多个子目录,主要包含以下核心目录:
|
目录 |
用途 |
|
/sys/devices |
所有物理设备的层次结构(按总线、设备类型组织)。 |
|
/sys/bus |
按总线类型(如 pci、usb、i2c)分类的设备。 |
|
/sys/class |
按功能分类的设备(如 net、input、block),符号链接到 /sys/devices。 |
|
/sys/block |
块设备(如硬盘、分区)。 |
|
/sys/module |
已加载的内核模块信息。 |
|
/sys/power |
电源管理(如休眠、唤醒)。 |
|
/sys/kernel |
内核运行时参数(如调试选项)。 |
|
/sys/firmware |
固件信息(如 ACPI、DTB)。 |
四、深入理解sysfs
Linux 设备模型通过 kobject、kset 和 kobj_type 三个核心对象实现设备层次结构的管理和 sysfs 的集成。所以要深入理解 sysfs,就必须要分析这三个结构体。
4.1. kobject(内核对象基础)
作用:
-
设备模型的基石:所有内核设备、驱动、总线的抽象基类。
-
sysfs 的桥梁:每个kobject对应sysfs中的一个目录(如 /sys/devices/...)。
-
引用计数:通过 kref 管理生命周期,自动释放资源。
struct kobject {const char *name; //对象名称(对应sysfs目录名)struct list_head entry; //链表节点(用于挂载到kset)struct kobject *parent; //父对象(形成层次结构)struct kset *kset; //所属的ksetstruct kobj_type *ktype; //对象类型(定义行为)struct kernfs_node *sd; //sysfs目录节点struct kref kref; //引用计数#ifdef CONFIG_DEBUG_KOBJECT_RELEASEstruct delayed_work release;#endifunsigned int state_initialized:1;unsigned int state_in_sysfs:1;unsigned int state_add_uevent_sent:1;unsigned int state_remove_uevent_sent:1;unsigned int uevent_suppress:1;};
4.2. kset(对象集合)
作用:
-
管理同类kobject:将多个kobject组织成集合。
-
事件通知:支持热插拔事件(uevent)的广播。
-
默认kobj_type:可为集合内的kobject提供统一行为。
提示:
-
默认kobj_type指: 当添加到kset中的 kobject 没有指定kobj_type时,则继承kset的kobj_type(kset 结构体中包含了一个 kobject 对象)。
-
在sysfs中,kset也是对应着一个目录(通过内嵌的 kobject 实现),但是目录下面包含着其他的kobject。
struct kset {struct list_head list; //kobject链表头spinlock_t list_lock; //保护链表的锁struct kobject kobj; //内嵌kobject(表示集合本身)const struct kset_uevent_ops *uevent_ops; //事件回调};
4.3. kobj_type(对象类型)
作用:
-
定义 kobject 的行为:
-
释放函数:当引用计数归零时调用。
-
sysfs 操作:指定属性的默认文件操作(sysfs_ops)。
-
属性表:定义 sysfs 中的属性文件(如 value、state)。
-
struct kobj_type {void (*release)(struct kobject *kobj); //释放资源const struct sysfs_ops *sysfs_ops; //默认文件操作struct attribute **default_attrs; /* use default_groups instead */const struct attribute_group **default_groups; //默认属性组const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);const void *(*namespace)(struct kobject *kobj);void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);};
struct sysfs_ops {ssize_t (*show)(struct kobject *, struct attribute *, char *);ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);};
sysfs 操作回调函数
三者关系图:

五、属性(Attributes)
属性是 sysfs 中与内核对象(如设备、驱动)关联的数据文件,用于读写配置或状态信息。
struct attribute {const char *name; //属性名称umode_t mode; //权限#ifdef CONFIG_DEBUG_LOCK_ALLOCbool ignore_lockdep:1;struct lock_class_key *key;struct lock_class_key skey;#endif};//总线属性struct bus_attribute {struct attribute attr;ssize_t (*show)(struct bus_type *bus, char *buf);ssize_t (*store)(struct bus_type *bus, const char *buf, size_t count);};//类属性struct class_attribute {struct attribute attr;ssize_t (*show)(struct class *class, struct class_attribute *attr,char *buf);ssize_t (*store)(struct class *class, struct class_attribute *attr,const char *buf, size_t count);};//设备属性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 driver_attribute {struct attribute attr;ssize_t (*show)(struct device_driver *driver, char *buf);ssize_t (*store)(struct device_driver *driver, const char *buf,size_t count);};
六、API函数
/*******************定义属性***********************///类属性#define CLASS_ATTR_RW(_name) \struct class_attribute class_attr_##_name = __ATTR_RW(_name)#define CLASS_ATTR_RO(_name) \struct class_attribute class_attr_##_name = __ATTR_RO(_name)#define CLASS_ATTR_WO(_name) \struct class_attribute class_attr_##_name = __ATTR_WO(_name)//总线属性#define BUS_ATTR_RW(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RW(_name)#define BUS_ATTR_RO(_name) \struct bus_attribute bus_attr_##_name = __ATTR_RO(_name)#define BUS_ATTR_WO(_name) \struct bus_attribute bus_attr_##_name = __ATTR_WO(_name)//驱动属性#define DRIVER_ATTR_RW(_name) \struct driver_attribute driver_attr_##_name = __ATTR_RW(_name)#define DRIVER_ATTR_RO(_name) \struct driver_attribute driver_attr_##_name = __ATTR_RO(_name)#define DRIVER_ATTR_WO(_name) \struct driver_attribute driver_attr_##_name = __ATTR_WO(_name)//设备属性#define DEVICE_ATTR(_name, _mode, _show, _store) \struct device_attribute dev_attr_##_name = __ATTR(_name, _mode, _show, _store)#define DEVICE_ATTR_RW(_name) \struct device_attribute dev_attr_##_name = __ATTR_RW(_name)#define DEVICE_ATTR_RO(_name) \struct device_attribute dev_attr_##_name = __ATTR_RO(_name)#define DEVICE_ATTR_WO(_name) \struct device_attribute dev_attr_##_name = __ATTR_WO(_name)
通过上面的宏,我们就能看到变量为:class_attr_xxx / bus_attr_xxx / driver_attr_xxx / dev_attr_xxx 。所以在调用其他函数接口的时候,注意一下变量名。
平常我们用的比较多的是设备属性,其他属性一般用的比较少。
/********************kobject相关*************************//*** 初始化kobject并添加到sysfs** 参数说明:** kobj:要初始化的kobject** ktype:传给kobj的kobj_type对象** parent:父kobject (父目录)** fmt: kobject名称*/int kobject_init_and_add(struct kobject *kobj,struct kobj_type *ktype, struct kobject *parent,const char *fmt, ...);/*** 创建一个kobject结构并注册到sysfs** 参数说明:** name:kobject名称** parent:父kobject (父目录)*/struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)/*** 减少kobject的引用计数,计数为0时触发release回调释放资源*/void kobject_put(struct kobject *kobj);/*** 从sysfs中移除kobject(但不释放内存)*/void kobject_del(struct kobject *kobj);/********************kset相关*************************//*** 创建kset并添加到sysfs** 参数说明:** name:kset名称** u:事件回调** parent_kobj:父kobject (父目录)*/struct kset *kset_create_and_add(const char *name,const struct kset_uevent_ops *u,struct kobject *parent_kobj);/*** 卸载kset并移除其sysfs目录*/void kset_unregister(struct kset *kset);
上面主要是 kobject 和 kset 的操作。
/******************属性操作***********************//*** 为kobject创建单个属性文件** 参数说明:** kobj: 为该kobject创建属性文件** attr: 要创建的属性*/int sysfs_create_file(struct kobject *kobj,const struct attribute *attr)/*** 移除属性文件*/void sysfs_remove_file(struct kobject *kobj,const struct attribute *attr)/*** 创建一组属性** 参数说明:** grp: 属性组*/int sysfs_create_group(struct kobject *kobj,const struct attribute_group *grp)/*** 移除属性组*/void sysfs_remove_group(struct kobject *kobj,const struct attribute_group *grp)
上面是通用的属性接口,就是不同内核对象(Device/Class/Bus/Driver)都可以使用。
/*****************Device类型属性操作******************//*** 为设备添加属性** 参数说明:** device: 设备对象** entry: 设备属性*/int device_create_file(struct device *device,const struct device_attribute *entry);/*** 移除设备属性*/void device_remove_file(struct device *dev,const struct device_attribute *attr);/*****************Class类型属性操作******************//*** 为类添加属性** 参数说明:** class: 类对象** attr: 类属性*/int class_create_file(struct class *class,const struct class_attribute *attr)/*** 移除类属性*/void class_remove_file(struct class *class,const struct class_attribute *attr)/*****************Bus类型属性操作******************//*** 为总线添加属性** 参数说明:** bus: 总线对象** attr: 总线属性*/int bus_create_file(struct bus_type *bus,struct bus_attribute *attr)/*** 移除总线属性*/void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)/*****************Driver类型属性操作******************//*** 为驱动添加属性** 参数说明:** driver: 驱动对象** attr: 驱动属性*/int driver_create_file(struct device_driver *driver,const struct driver_attribute *attr)/*** 移除驱动属性*/void driver_remove_file(struct device_driver *driver,const struct driver_attribute *attr)
八、驱动示例
#include<linux/module.h>#include<linux/fs.h>#include<linux/device.h>#include<linux/kernel.h>#include<linux/uaccess.h>#include<linux/slab.h>#define DEVICE_NAME "sysfs_demo"#define CLASS_NAME "sysfs"static int major_num;static struct class *sysfs_class =NULL;static struct device *sysfs_device =NULL;// 驱动私有数据static int current_value = 0;//-----------------------------------------// sysfs 属性操作//-----------------------------------------// 读取属性(cat /sys/.../value)static ssize_t value_show(struct device *dev,struct device_attribute *attr,char *buf) {return sprintf(buf, "%d\n", current_value);}// 写入属性(echo 123 > /sys/.../set_value)static ssize_t set_value_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count) {int new_value;if (kstrtoint(buf, 10, &new_value) != 0)return -EINVAL;current_value = new_value;printk(KERN_INFO "Value updated to %d\n", current_value);return count;}// 定义 sysfs 属性static DEVICE_ATTR_RO(value); // 只读属性static DEVICE_ATTR_WO(set_value); // 只写属性// 属性列表static struct attribute *sysfs_attrs[] = {&dev_attr_value.attr,&dev_attr_set_value.attr,NULL,};// 属性组static struct attribute_group sysfs_attr_group = {.name = "demo", //子目录名(/sys/class/sysfs/sysfs_demo/demo/).attrs = sysfs_attrs,};//-----------------------------------------// 字符设备操作//-----------------------------------------static int device_open(struct inode *inode, struct file *file){printk(KERN_INFO "Device opened\n");return 0;}static ssize_t device_read(struct file *file, char __user *buf,size_t len, loff_t *offset) {char message[32];int ret;snprintf(message, sizeof(message), "Current value: %d\n", current_value);ret = copy_to_user(buf, message, strlen(message));return ret ? -EFAULT : strlen(message);}static struct file_operations fops = {.open = device_open,.read = device_read,};//-----------------------------------------// 模块初始化和退出//-----------------------------------------static int __init sysfs_demo_init(void){// 1. 注册字符设备major_num = register_chrdev(0, DEVICE_NAME, &fops);if (major_num < 0) {printk(KERN_ALERT "Failed to register device\n");return major_num;}// 2. 创建设备类(/sys/class/sysfs/)sysfs_class = class_create(THIS_MODULE, CLASS_NAME);if (IS_ERR(sysfs_class)) {unregister_chrdev(major_num, DEVICE_NAME);return PTR_ERR(sysfs_class);}// 3. 创建设备节点(/dev/sysfs_demo)sysfs_device = device_create(sysfs_class, NULL, MKDEV(major_num, 0),NULL, DEVICE_NAME);if (IS_ERR(sysfs_device)) {class_destroy(sysfs_class);unregister_chrdev(major_num, DEVICE_NAME);return PTR_ERR(sysfs_device);}// 4. 创建 sysfs 属性组(/sys/class/sysfs/sysfs_demo/demo/)if (sysfs_create_group(&sysfs_device->kobj, &sysfs_attr_group)) {device_destroy(sysfs_class, MKDEV(major_num, 0));class_destroy(sysfs_class);unregister_chrdev(major_num, DEVICE_NAME);return -ENOMEM;}printk(KERN_INFO "sysfs_demo: Loaded (major=%d)\n", major_num);return 0;}static void __exit sysfs_demo_exit(void){// 清理资源sysfs_remove_group(&sysfs_device->kobj, &sysfs_attr_group);device_destroy(sysfs_class, MKDEV(major_num, 0));class_destroy(sysfs_class);unregister_chrdev(major_num, DEVICE_NAME);printk(KERN_INFO "sysfs_demo: Unloaded\n");}module_init(sysfs_demo_init);module_exit(sysfs_demo_exit);MODULE_LICENSE("GPL");
测试:
# 查看属性ls /sys/class/sysfs/sysfs_demo/demo/cat /sys/class/sysfs/sysfs_demo/demo/value# 修改属性echo 42 > /sys/class/sysfs/sysfs_demo/demo/set_valuecat /sys/class/sysfs/sysfs_demo/demo/value # 确认值已更新
精简版:
#include<linux/module.h>#include<linux/device.h>#include<linux/fs.h>#include<linux/slab.h>#define DEVICE_NAME "sysfs_simple"#define CLASS_NAME "sysfs_simple"static int major_num;static struct class *sysfs_class =NULL;static struct device *sysfs_device =NULL;static int current_value = 0;//-----------------------------------------// sysfs 属性操作//-----------------------------------------// 读取属性(cat /sys/.../value)static ssize_t value_show(struct device *dev,struct device_attribute *attr,char *buf) {return sprintf(buf, "%d\n", current_value);}// 写入属性(echo 123 > /sys/.../set_value)static ssize_t set_value_store(struct device *dev,struct device_attribute *attr,const char *buf, size_t count) {int ret;ret = kstrtoint(buf, 10, ¤t_value);if (ret < 0)return ret;printk(KERN_INFO "Value set to %d\n", current_value);return count;}// 定义属性(不再需要 attribute_group)static DEVICE_ATTR(value, 0444, value_show, NULL); // 只读static DEVICE_ATTR(set_value, 0200, NULL, set_value_store); // 只写//-----------------------------------------// 模块初始化和退出//-----------------------------------------static int __init sysfs_simple_init(void){int ret;// 1. 注册字符设备(可选,仅演示 sysfs)major_num = register_chrdev(0, DEVICE_NAME, NULL);if (major_num < 0)return major_num;// 2. 创建设备类和设备节点sysfs_class = class_create(THIS_MODULE, CLASS_NAME);if (IS_ERR(sysfs_class)) {ret = PTR_ERR(sysfs_class);goto fail_chrdev;}sysfs_device = device_create(sysfs_class, NULL, MKDEV(major_num, 0),NULL, DEVICE_NAME);if (IS_ERR(sysfs_device)) {ret = PTR_ERR(sysfs_device);goto fail_class;}// 3. 直接创建 sysfs 属性文件ret = device_create_file(sysfs_device, &dev_attr_value);if (ret < 0)goto fail_device;ret = device_create_file(sysfs_device, &dev_attr_set_value);if (ret < 0)goto fail_remove_value;printk(KERN_INFO "sysfs_simple: Loaded (major=%d)\n", major_num);return 0;fail_remove_value:device_remove_file(sysfs_device, &dev_attr_value);fail_device:device_destroy(sysfs_class, MKDEV(major_num, 0));fail_class:class_destroy(sysfs_class);fail_chrdev:unregister_chrdev(major_num, DEVICE_NAME);return ret;}static void __exit sysfs_simple_exit(void){device_remove_file(sysfs_device, &dev_attr_set_value);device_remove_file(sysfs_device, &dev_attr_value);device_destroy(sysfs_class, MKDEV(major_num, 0));class_destroy(sysfs_class);unregister_chrdev(major_num, DEVICE_NAME);printk(KERN_INFO "sysfs_simple: Unloaded\n");}module_init(sysfs_simple_init);module_exit(sysfs_simple_exit);MODULE_LICENSE("GPL");
测试:
# 查看只读属性cat /sys/class/sysfs_simple/sysfs_simple/value# 修改只写属性echo 42 > /sys/class/sysfs_simple/sysfs_simple/set_value

958

被折叠的 条评论
为什么被折叠?



