当前位置: 首页 > article >正文

Linux configfs和sysfs的使用与理解

目录

1 概述

2 configfs与sys

3 configfs

3.1 configfs介绍

3.2 基本使用结构原理

3.3 使用与示例

4 sysfs

4.1 sysfs文件系统介绍

4.2 示例

5 引用参考


1 概述

        本文介绍了configfs和sys的区别与使用流程,以实例来分析使用过程。

2 configfs与sys

        configfs是基于ram的文件系统,这一点与sysfs很像,但是与sysfs不同之处在于,configfs可以通过在用户态的操作创建和删除内核对象,而我们一般使用sysfs是已经有内核对象之后,再去创建sysfs文件节点。从使用上看都可以通过read write读取/修改文件属性,重点在于sysfs的内核对象是在内核创建、销毁的,sysfs是访问这些内核对象的窗口。但是configfs的内核对象的创建/销毁是由用户态的mkdir等操作完成,用户执行mkdir会在内核创建一个config_item对象,用户执行rmdir会销毁这个对象。

        注:来自https://www.kernel.org/doc/Documentation/filesystems/configfs/configfs.txt

3 configfs

3.1 configfs介绍

        是一种基于ram的文件系统,在用户空间配置内核对象,可直接察看,通过用户态目录文件访问接口,适用于内核对象有众多复杂的配置。一般当内核需要很多参数需要配置时或者当需要动态创建内核对象并且内核对象需要修改配置时,会考虑用configfs实现。

3.2 基本使用结构原理

        顶层结构是struct configfs_subsystem,为configfs子系统结构:

struct configfs_subsystem {
    struct config_group su_group;
    struct mutex        su_mutex;
};

        接着是struct config_group,是configfs目录和属性的容器,即group下不仅可以有item,也可以链接下一级group,后面章节示例。

struct config_group {
    struct config_item      cg_item;
    struct list_head        cg_children;
    struct configfs_subsystem   *cg_subsys;
    struct list_head        default_groups;
    struct list_head        group_entry;
};

        struct config_item是configfs目录,代表可配置的内核对象

struct config_item {
    char            *ci_name;
    char            ci_namebuf[CONFIGFS_ITEM_NAME_LEN];
    struct kref     ci_kref;
    struct list_head    ci_entry;
    struct config_item  *ci_parent;
    struct config_group *ci_group;
    const struct config_item_type   *ci_type;
    struct dentry       *ci_dentry;
};

        struct configfs_attribute是目录下面的属性:

struct configfs_attribute {
    const char      *ca_name;
    struct module       *ca_owner;
    umode_t         ca_mode;
    ssize_t (*show)(struct config_item *, char *);
    ssize_t (*store)(struct config_item *, const char *, size_t);
};

3.3 使用与示例

        使用configfs功能需要打开内核配置支持CONFIG_CONFIGFS_FS=y

        一般情况下需要先注册系统 configfs_register_subsystem

static int __init testfs_init(void)
{
    config_group_init(&testfs_subsys.su_group);
    return configfs_register_subsystem(&testfs_subsys);
}

        testfs_subsys是

static struct configfs_group_operations testfs_ops = {
    .make_group = &testfs_make,
    .drop_item = &testfs_drop,
};
static struct config_item_type testfs_type = {
    .ct_group_ops = &testfs_ops,
    .ct_owner   = THIS_MODULE,
};
static struct configfs_subsystem testfs_subsys = {
    .su_group = {
        .cg_item = {
            .ci_namebuf = "test",
            .ci_type = &testfs_type,
        },
    },
    .su_mutex = __MUTEX_INITIALIZER(testfs_subsys.su_mutex),
};

        当用户在挂载configfs文件夹创建文件夹时,configfs将在该文件夹下创建test文件夹,并调用testfs_make回调。

        testfs_make做了两个操作,一个是在顶层group下初始化创建item,另外一个是在顶层group下初始化创建另外一个group。

static struct config_group *testfs_make(struct config_group *group, const char *name)
{
    configfs_value *tcv = NULL;

    tcv = kzalloc(sizeof(configfs_value), GFP_KERNEL);
    if (!tcv)
        return ERR_PTR(-ENOMEM);
    printk("top group name is %s\n", name);
    //顶层group下初始化item,test_root_type里定义定义的item属性都是文件
    config_group_init_type_name(&tcv->top_group_p, name, &test_root_type);
    //在顶层group下再定义一层group,此时对应的group为文件夹,名称为secdir
    config_group_init_type_name(&tcv->a2_group_p, "secdir", &a2_group_type);
    configfs_add_default_group(&tcv->a2_group_p, &tcv->top_group_p);
    return &tcv->top_group_p;
}

        先看在顶层group下创建初始化item过程。

        test_root_type定义        

CONFIGFS_ATTR(test_dev_desc_, a1);
static struct configfs_attribute *test_root_attrs[] = {
    &test_dev_desc_attr_a1,
    NULL,
};
static struct configfs_item_operations test_root_item_ops = {
    .release                = test_info_attr_release,
};
static const struct config_item_type test_root_type = {
    .ct_item_ops    = &test_root_item_ops,
    .ct_attrs   = test_root_attrs,
    .ct_owner   = THIS_MODULE,
};

        其中CONFIGS_ATTR定义如下

#define CONFIGFS_ATTR(_pfx, _name)          \
static struct configfs_attribute _pfx##attr_##_name = { \
    .ca_name    = __stringify(_name),       \
    .ca_mode    = S_IRUGO | S_IWUSR,        \
    .ca_owner   = THIS_MODULE,          \
    .show       = _pfx##_name##_show,       \
    .store      = _pfx##_name##_store,      \
}

        展开后,可知属性a1最终绑定一个show和store函数回调,分别在上层用户读和写调用。

show回调函数只是简单的返回进行打印显示

static ssize_t test_dev_desc_a1_show(struct config_item *item, char *page)
{
    configfs_value *tcv = to_configfs_value(item);

    printk("%s:%d called\n", __func__, __LINE__);
    return sprintf(page, "%s\n", tcv->a1str);
}

store函数将用户配置的值更新到内存变量中。

static ssize_t test_dev_desc_a1_store(struct config_item *item,
        const char *page, size_t len)
{
    configfs_value *tcv = to_configfs_value(item);
    size_t cplen = len>sizeof(tcv->a1str)?sizeof(tcv->a1str):len;
    printk("%s:%d called\n", __func__, __LINE__);
    snprintf(tcv->a1str, cplen, "%s", page);
    return cplen;
}

        最后分析在group下创建另外一个group,再在新建group下创建item属性。前面make函数展示了初始化新建group的过程

    //在顶层group下再定义一层group,此时对应的group为文件夹,名称为secdir
    config_group_init_type_name(&tcv->a2_group_p, "secdir", &a2_group_type);
    configfs_add_default_group(&tcv->a2_group_p, &tcv->top_group_p);

        a2_group_type定义如下

static struct configfs_group_operations a2gitem_ops = {
    .make_group     = &function_make,
    .drop_item      = &function_drop,
};
static const struct config_item_type a2_group_type = {
    .ct_group_ops   = &a2gitem_ops,
    .ct_owner       = THIS_MODULE,
};

        跟前面是一样,当用户在secdir文件夹下创建文件时,function_make回调将被调用,该函数同样初始化了一个item

static struct config_group *function_make(
        struct config_group *group,
        const char *name)
{
    struct config_group *agroup;

    printk("%s:%d (%s) called\n", __func__, __LINE__, name);
    
    agroup = kzalloc(sizeof(struct config_group), GFP_KERNEL);
    if (!agroup)
        return ERR_PTR(-ENOMEM);
    
    config_group_init_type_name(agroup, name, &f_uac2_func_type);
    return agroup;
}

        Item下的定义和调用流程跟之前a1值一样,这里是a2值,将在secdir下生成一个a2文件。

static struct configfs_item_operations f_uac2_item_ops = {
    .release    = f_uac2_attr_release,
};
CONFIGFS_ATTR(test_dev_desc_, a2);
static struct configfs_attribute *f_uac2_attrs[] = {
    &test_dev_desc_attr_a2,
    NULL
};
static const struct config_item_type f_uac2_func_type = {
    .ct_item_ops    = &f_uac2_item_ops,
    .ct_attrs   = f_uac2_attrs,
    .ct_owner   = THIS_MODULE,
};

        最后是示例运行的操作过程(在qemu模拟环境下测试,qemu环境搭建参考笔者之前写的文章https://blog.csdn.net/tang_vincent/article/details/144562576?spm=1001.2014.3001.5501):

        文件生成情况

        完整代码

/*************************************************************************
 * File Name: configfs.c
 * Author: vincent 
 * Mail:
 * Created Time: 2024年12月13日 星期五 11时09分59秒
 ************************************************************************/
#include <linux/configfs.h>
#include <linux/module.h>
#include <linux/slab.h>
#include <linux/device.h>

typedef struct test_configfs_value {
    char a1str[128];
    char a2str[128];
    struct config_group top_group_p;
    struct config_group a2_group_p;
}configfs_value;
static inline struct test_configfs_value *to_configfs_value(struct config_item *item)
{
    return container_of(to_config_group(item), struct test_configfs_value, top_group_p);
}
/*********定义在顶层group的item配置*********/
static ssize_t test_dev_desc_a1_show(struct config_item *item, char *page)
{
    configfs_value *tcv = to_configfs_value(item);
    printk("%s:%d called\n", __func__, __LINE__);
    return sprintf(page, "%s\n", tcv->a1str);
}
static ssize_t test_dev_desc_a1_store(struct config_item *item,
        const char *page, size_t len)
{
    configfs_value *tcv = to_configfs_value(item);
    size_t cplen = len>sizeof(tcv->a1str)?sizeof(tcv->a1str):len;
    printk("%s:%d called\n", __func__, __LINE__);
    snprintf(tcv->a1str, cplen, "%s", page);
    return cplen;
}
static void test_info_attr_release(struct config_item *item)
{
    printk("release attr res\n");
}
CONFIGFS_ATTR(test_dev_desc_, a1);
static struct configfs_attribute *test_root_attrs[] = {
    &test_dev_desc_attr_a1,
    NULL,
};
static struct configfs_item_operations test_root_item_ops = {
    .release                = test_info_attr_release,
};
static const struct config_item_type test_root_type = {
    .ct_item_ops    = &test_root_item_ops,
    .ct_attrs   = test_root_attrs,
    .ct_owner   = THIS_MODULE,
};
/*****************************************************/
/**********在顶层grop下又定义了一层group配置************/
static ssize_t test_dev_desc_a2_show(struct config_item *item, char *page)
{
    configfs_value *tcv = to_configfs_value(item);
    printk("%s:%d called\n", __func__, __LINE__);
    return sprintf(page, "%s\n", tcv->a1str);
}
static ssize_t test_dev_desc_a2_store(struct config_item *item,
        const char *page, size_t len)
{
    configfs_value *tcv = to_configfs_value(item);
    size_t cplen = len>sizeof(tcv->a1str)?sizeof(tcv->a1str):len;
    printk("%s:%d called\n", __func__, __LINE__);
    snprintf(tcv->a1str, cplen, "%s", page);
    return cplen;
}
static void f_uac2_attr_release(struct config_item *item)
{
    printk("%s:%d called\n", __func__, __LINE__);
}
static struct configfs_item_operations f_uac2_item_ops = {
    .release    = f_uac2_attr_release,
};
CONFIGFS_ATTR(test_dev_desc_, a2);
static struct configfs_attribute *f_uac2_attrs[] = {
    &test_dev_desc_attr_a2,
    NULL
};
static const struct config_item_type f_uac2_func_type = {
    .ct_item_ops    = &f_uac2_item_ops,
    .ct_attrs   = f_uac2_attrs,
    .ct_owner   = THIS_MODULE,
};
static struct config_group *function_make(
        struct config_group *group,
        const char *name)
{
    struct config_group *agroup;
    printk("%s:%d (%s) called\n", __func__, __LINE__, name);
    
    agroup = kzalloc(sizeof(struct config_group), GFP_KERNEL);
    if (!agroup)
        return ERR_PTR(-ENOMEM);
    
    config_group_init_type_name(agroup, name, &f_uac2_func_type);
    return agroup;
}
static void function_drop(
        struct config_group *group,
        struct config_item *item)
{
    printk("%s:%d called\n", __func__, __LINE__);
}
static struct configfs_group_operations a2gitem_ops = {
    .make_group     = &function_make,
    .drop_item      = &function_drop,
};
static const struct config_item_type a2_group_type = {
    .ct_group_ops   = &a2gitem_ops,
    .ct_owner       = THIS_MODULE,
};
/*****************************************************/
static struct config_group *testfs_make(struct config_group *group, const char *name)
{
    configfs_value *tcv = NULL;
    tcv = kzalloc(sizeof(configfs_value), GFP_KERNEL);
    if (!tcv)
        return ERR_PTR(-ENOMEM);
    printk("top group name is %s\n", name);
    //顶层group下初始化item,test_root_type里定义定义的item属性都是文件
    config_group_init_type_name(&tcv->top_group_p, name, &test_root_type);
    //在顶层group下再定义一层group,此时对应的group为文件夹,名称为secdir
    config_group_init_type_name(&tcv->a2_group_p, "secdir", &a2_group_type);
    configfs_add_default_group(&tcv->a2_group_p, &tcv->top_group_p);
    return &tcv->top_group_p;
}
static void testfs_drop(struct config_group *group, struct config_item *item)
{
    config_item_put(item);
}
static struct configfs_group_operations testfs_ops = {
    .make_group = &testfs_make,
    .drop_item = &testfs_drop,
};
static struct config_item_type testfs_type = {
    .ct_group_ops = &testfs_ops,
    .ct_owner   = THIS_MODULE,
};
static struct configfs_subsystem testfs_subsys = {
    .su_group = {
        .cg_item = {
            .ci_namebuf = "test",
            .ci_type = &testfs_type,
        },
    },
    .su_mutex = __MUTEX_INITIALIZER(testfs_subsys.su_mutex),
};
static int __init testfs_init(void)
{
    config_group_init(&testfs_subsys.su_group);
    return configfs_register_subsystem(&testfs_subsys);
}
static void __exit testfs_exit(void)
{
    configfs_unregister_subsystem(&testfs_subsys);
}
module_init(testfs_init);
module_exit(testfs_exit);
MODULE_LICENSE("GPL v2");

4 sysfs

4.1 sysfs文件系统介绍

        sysfs文件系统是Linux 2.6版本开始加入的,它的作用是将设备和驱动程序的信息导出到用户空间,同时支持修改和调整,方便了用户读取修改设备信息。

        sysfs作为Linux内核中的一种特殊虚拟文件系统,它用来向用户空间提供内核设备和驱动程序的信息。因此,sysfs是一种具体的文件系统实现,它利用了VFS提供的抽象接口来展示内核数据。通过sysfs,用户可以统一地访问不同类型的内核信息,而不需要关心底层实现细节。Sysfs内容会随着系统硬件配置的变化动态更新。例如,插拔设备会导致相应的sysfs目录和文件创建或删除。

4.2 示例

        模块加载函数中,使用了sysfs_create_file和sysfs_create_group分别创建/sys/kernel下的testsysfs文件夹和/sys/下的group_test文件夹。

static int __init testsysfs_init(void)
{
    int ret;

    // 创建 kobject
    testsysfs_kobj = kobject_create_and_add("testsysfs", kernel_kobj);
    if (!testsysfs_kobj) {
        pr_err("Failed to create kobject\n");
        return -ENOMEM;
    }
    // 创建 sysfs 属性
    ret = sysfs_create_file(testsysfs_kobj, &sysfsdrv_attr.attr);
    if (ret) {
        pr_err("Failed to create sysfs file\n");
        kobject_put(testsysfs_kobj);
        return ret;
    }
    //使用sysfs group方式创建sysfs
    group_kob = kobject_create_and_add("group_test",kernel_kobj->parent);
    ret = sysfs_create_group(group_kob, &sysfs_attr_g);
    if (ret) {
        pr_err("Failed to create sysfs group\n");
        kobject_put(group_kob);
        return ret;
    }
    return 0;
}

        对于在/sys/kernel 创建文件夹,定义了测试变量test_string,和对应的读写回调接口

static char test_string[64] = "i am test string"; // 初始化一个全局字符串变量
// sysfs 读取时的回调函数
static ssize_t sysfsdrv_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
    return scnprintf(buf, 64, "%s\n", test_string);
}
// sysfs 写入回调函数
static ssize_t sysfsdrv_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
    if (count >= 64) {
        return -EINVAL; // 如果写入的数据太长,返回错误
    }
 
    strncpy(test_string, buf, count); // 复制用户输入的字符串
    test_string[count - 1] = '\0'; // 确保字符串以空字符结尾
 
    return count; // 返回写入的字节数
}
// 定义 sysfs 属性
static struct kobj_attribute sysfsdrv_attr = __ATTR(test_string, 0664, sysfsdrv_show, sysfsdrv_store);
// 定义 kobject
static struct kobject *testsysfs_kobj;

        对于在/sys/新建文件夹,定义两个变量,一个只读,一个可读写。

static int gvalue_status = 0;
static int gvalue = 11;
static ssize_t gvalue_status_show(struct kobject* kobjs,struct kobj_attribute *attr,char *buf)
{
    printk(KERN_INFO "gvalue status show\n");
    return sprintf(buf,"gvalue status : \n%d\n", gvalue_status);
}
static ssize_t gvalue_status_store(struct kobject *kobj, struct kobj_attribute *attr,const char *buf, size_t count)
{
    printk(KERN_INFO "gvalue status store %s,%d\n", buf, gvalue_status);
    gvalue_status = !gvalue_status;
    return count;
}
static ssize_t gvalue_show(struct kobject* kobjs,struct kobj_attribute *attr,char *buf)
{
    printk(KERN_INFO "Read gvalue\n");
    return sprintf(buf, "The gvalue = %d\n", gvalue);
}
static struct kobj_attribute status_attr = __ATTR_RO(gvalue);
static struct kobj_attribute value_attr = __ATTR(gvalue_status, 0660, gvalue_status_show, gvalue_status_store);
static struct attribute *test_attrs[] = {
    &status_attr.attr,
    &value_attr.attr,
    NULL,
};
static struct attribute_group sysfs_attr_g = {
    .name = "kobject_test",
    .attrs = test_attrs,
};

5 引用参考

Linux驱动开发笔记(十三)Sysfs文件系统_linux sysfs-CSDN博客

https://zhuanlan.zhihu.com/p/662592368

https://www.kernel.org/doc/Documentation/filesystems/configfs/configfs.txt
使用qemu搭建armv7嵌入式开发环境-CSDN博客

01.坐标系介绍_哔哩哔哩_bilibili


http://www.kler.cn/a/452242.html

相关文章:

  • rabbitmq相关使用
  • Unity3D Huatuo技术原理剖析详解
  • windows C++ TCP客户端
  • Unity中LineRenderer使用MeshCollider方法参考
  • Pion WebRTC 项目教程
  • js 数据类型以及typeof的关系
  • 开关电源中的高频振荡噪声及其抑制方法
  • 117.【C语言】数据结构之排序(选择排序)
  • 青蛇人工智能学家
  • 2025差旅平台怎么选?一体化、全流程降本案例解析
  • 用 Python 从零开始构建 LLaMA 3
  • 网络管理(Network Management,NM)(一)
  • 【唐叔学算法】第19天:交换排序-冒泡排序与快速排序的深度解析及Java实现
  • 斐波那契数【东北大学oj数据结构10-1】C++
  • 大数据-259 离线数仓 - Griffin架构 修改配置 pom.xml sparkProperties 编译启动
  • Type-c接口
  • 将Minio设置为Django的默认Storage(django-storages)
  • 深度学习中常见的权重初始化方法
  • 关于 [MenuItem] Hierarchy 右键扩展多选问题
  • linux查看天气预报
  • Canvas指定三角形内部生成随机点
  • GoFrame框架介绍
  • 宏定义介绍
  • mysql双主双从
  • 《Mycat核心技术》第06章:Mycat问题处理总结
  • 短视频矩阵系统的视频批量剪辑源码技术开发,支持OEM