虚拟文件系统之sysfs

一、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 设备模型通过 kobjectkset 和 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;      //所属的kset  struct kobj_type  *ktype;     //对象类型(定义行为)  struct kernfs_node  *sd;      //sysfs目录节点  struct kref        kref;      //引用计数#ifdef CONFIG_DEBUG_KOBJECT_RELEASE  struct delayed_work  release;#endif  unsigned 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提供统一行为。

提示:

  1. 默认kobj_type指: 当添加到kset中的 kobject 没有指定kobj_type时,则继承kset的kobj_type(kset 结构体中包含了一个 kobject 对象)。

  2. 在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_ALLOC  bool      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, &current_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
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值