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

linux usb 驱动 - configfs 文件系统

linux usb hcd 驱动框架

    • 一、数据结构
      • 1. configfs_subsystem
      • 2. config_group
      • 3. config_item
      • 4. config_item_type
      • 5. configfs_attribute
      • 6. configfs_dirent
      • 7. 数据结构关系
    • 二、根目录的注册
      • 1. configfs_register_subsystem
        • 1) configfs_create
        • 2) configfs_create_dir
      • 2. 编程实验
      • 3. populate_attrs
    • 三、创建子目录
    • 四、创建属性文件

作者: baron
个人博客: baron-z.cn

usb 驱动集合:
linux usb 驱动 - hcd 驱动框架
linux usb 驱动 - configfs 文件系统
linux usb 驱动 - udc 驱动框架

    configfs 是 Linux 内核中的一个虚拟文件系统(vfs),他和 sysfs 类似, 都是以目录文件的形式导出到用户空间和用户交互, 虚拟表示 configfs 中的"目录"只是一个用户界面的表现形式, 没有实际的物理存储空间分配, 目录结构只是内核对象的一种层次化展示方式. 每个"目录"实际上映射到一个 config_group, 目录操作被转换为对应的 config_group 操作, 比如 mkdir 实际上是调用 make_group 来创建新的内核配置项. 文件系统操作都被转换为对应的内核对象操作,通过这些回调接口来实现用户空间和内核空间的交互.

    由于 configfs 是 usb udc 驱动的前置部分, 因此虽然是文件系统的内容, 但是还是分类 usb 在驱动中. 在内核中 configfs 几乎只用于配置 usb.

这种设计的优点:

  • 提供了一个直观的用户接口(通过目录结构), linux 和核心理念一切皆文件
  • 实现了用户空间和内核空间的优雅交互
  • 便于管理和配置内核对象

这就是为什么它叫 configfs (配置文件系统) 而不是普通的文件系统,它的主要目的是配置而不是存储.下面命令用于挂载 configfs 文件系统.

mount -t configfs none /sys/kernel/config

    在 vfs(虚拟文件系统) 文件系统中, 文件和目录的描述主要由 dentry(目录项) 和 inode(索引节点) 描述. 目录/文件 = dentry + inode

struct dentry {

    ......
    struct inode *d_inode; // 其关联的 inode
    void *d_fsdata;        // 在configfs 中指向 configfs_dirent
    ......
};

    dentry 是目录树的结构化表示, 用于描述文件系统中的路径信息, 它是文件路径的一个缓存, 记录了目录树中的父子关系, 用于高效地解析路径和快速定位文件.

struct inode {
    ......
    umode_t         i_mode; // 文件的类型和访问权限(如 S_IFREG 表示普通文件,S_IFDIR 表示目录)
    const struct inode_operations   *i_op;   // VFS 文件系统的回调接口,用于实现目录和文件操作,如 mkdir(创建目录)、rmdir(删除目录)、create(创建文件)、lookup(查找目录项)等。
    const struct file_operations    *i_fop; // 文件的操作接口,用于处理打开文件后的操作,如 read(读取文件)、write(写入文件)、release(关闭文件)等。
    ......
};

    inode 是文件或目录的核心元数据结构,

  • 它用于描述文件系统中的一个实际对象(文件或目录).
  • 它存储了文件的属性(如大小、权限、时间戳)和数据块位置等信息.
  • 每个文件或目录在文件系统中都有一个唯一的 inode.
  • dentry 可以有多个例如链接文件, 而 inode 只能有一个唯一的.
  • i_op 表示目录操作后面, i_fop 表示是文件操作, 目录也可以支持文件操作.
重点: i_mode 成员变量表明了这是个目录还是文件, 以及操作权限, 所以 注册目录还是文件就是设置 i_mode 这个成员变量, 无论是目录还是文件对应的底层操作都是一样的. 只是回调的接口不一样而已.

下表描述了 i_mode 的文件/目录等定义

文件类型宏定义值(八进制)描述
普通文件S_IFREG0100000标识普通文件
目录S_IFDIR0040000标识目录
符号链接S_IFLNK0120000标识符号链接
字符设备S_IFCHR0020000标识字符设备文件
块设备S_IFBLK0060000标识块设备文件
FIFO(管道)S_IFIFO0010000标识命名管道(FIFO)
套接字S_IFSOCK0140000标识套接字文件

此外,i_mode 还包括权限标志位,与文件类型标志位结合使用,用于表示文件的权限信息。

权限类型宏定义值(二进制)描述
读权限S_IRUSR0000400文件所有者读权限
写权限S_IWUSR0000200文件所有者写权限
执行权限S_IXUSR0000100文件所有者执行权限
读权限(组)S_IRGRP0000040所属组读权限
写权限(组)S_IWGRP0000020所属组写权限
执行权限(组)S_IXGRP0000010所属组执行权限
读权限(其他)S_IROTH0000004其他用户读权限
写权限(其他)S_IWOTH0000002其他用户写权限
执行权限(其他)S_IXOTH0000001其他用户执行权限

一、数据结构

1. configfs_subsystem

    用来描述 congfs 子系统的根节点, 每一个 configfs 文件系统都有且只有一个 configfs_subsystem 结构, 用来描述 /sys/kernel/config目录下的第一个节点. 它的 su_group成员就是根目录.

struct configfs_subsystem {
        struct config_group     su_group; // 根目录
        struct mutex            su_mutex;
};

2. config_group

    用来描述 configfs 中的目录管理结构, 包含真正的目录回调接口的描述结构 cg_item 和 default_groups 等数据结构, 用来管理当前目录以及当前目录包含的子目录. 其中 default_groups 用来描述当前目录下的子目录的管理结构.

struct config_group {
        struct config_item              cg_item;          // 真正的目录回调接口的描述结构
        struct list_head                cg_children;      // 连接子目录对应的 config_item
        struct configfs_subsystem       *cg_subsys;
        struct config_group             **default_groups; // 当前config_group 下的子 config group
};

3. config_item

    真正的目录回调接口描述结构, 可以理解为每一个 config_item 结构都对应着 configs 的一个目录/文件, 他抽象了真正的目录/文件的底层操作, 例如 mkdir 等操作最终都被映射成对这个数据结构的操作.

struct config_item {
        char                    *ci_name; // 目录名称
        char                    ci_namebuf[CONFIGFS_ITEM_NAME_LEN];
        struct kref             ci_kref;    // 引用计数
        struct list_head        ci_entry;   // 连接到所属的 group 的 cg_children
        struct config_item      *ci_parent; // 连接到父目录 
        struct config_group     *ci_group;  // 所属的 group
        struct config_item_type *ci_type;   // 
        struct dentry           *ci_dentry; // 连接到对应的目录结构 dentry
}

4. config_item_type

他有两个部分的内容

  • 目录操作: ct_group_ops用于描述目录的常见操作 mkdir, rmdir 等操作的回调接口, ct_item_ops则是让目录也支持 showstore的属性操作.
  • 属性文件描述数组: ct_attrs则表示当前目录下的属性文件的数组.当前文件下的属性文件都保存在这个数组中.
struct config_item_type {
        struct module                           *ct_owner;
        struct configfs_item_operations         *ct_item_ops;    // 用于描述目录的常见操作 mkdir, rmdir 等操作的回调接口
        struct configfs_group_operations        *ct_group_ops;   // 目录也支持 show 和 store 的属性操作
        struct configfs_attribute               **ct_attrs;      // 属性文件描述数组, 每一个成员都是一个属性文件回调接口.
};

5. configfs_attribute

    属性文件回述结构, 每一个结构包含一个属性文件的名字和回调函数.

struct configfs_attribute {
        const char              *ca_name;    // 属性文件的名字
        struct module           *ca_owner;   // 所属模块
        umode_t                 ca_mode;     // 操作权限
        ssize_t (*show)(struct config_item *, char *); // 对应的 show 回调函数
        ssize_t (*store)(struct config_item *, const char *, size_t); // 对应的 store 回调结构
};

6. configfs_dirent

    他是目录描述结构 dentry 和 configfs 中的各个数据结构的桥梁.内核通过 VFS(虚拟文件系统) 层访问 configfs 中的对象.

struct configfs_dirent {
        atomic_t                s_count;
        int                     s_dependent_count;
        struct list_head        s_sibling;
        struct list_head        s_children;
        struct list_head        s_links;
        void                    * s_element;
        int                     s_type;
        umode_t                 s_mode;
        struct dentry           * s_dentry;
        struct iattr            * s_iattr;
#ifdef CONFIG_LOCKDEP
        int                     s_depth;
#endif
};

7. 数据结构关系

    文件和目录都是由相同的数据结构组成, 怎么区分文件和目录呢, 通过两个部分, 一是 inode 的 i_mode 标志, 另一个就是 inode 的回调.

目录:
  • i_mode 设置 S_IFDIR
  • inode 设置回调
    
inode->i_op = &configfs_dir_inode_operations;
inode->i_fop = &configfs_dir_operations;
    
  

文件:
  • i_mode 设置 S_IFREG
  • inode 设置回调
    
inode->i_size = PAGE_SIZE;
inode->i_fop = &configfs_file_operations;
    
  

下图整理了configfs 文件系统的数据结构的关系
在这里插入图片描述

二、根目录的注册

1. configfs_register_subsystem

    configfs_register_subsystem 接口用于注册根目录, 调用该接口后, 我们将在 configfs 中创建 configfs_subsystem 数据结构描述的目录.

    整个注册过程就是填充 config_groupconfig_itemconfig_item_typeconfigfs_attribute 以及分配 configfs_direntdentryinode 等数据结构, 并将其关联起来.调用流程如下图所示.

在这里插入图片描述

    整个注册过程大体分可以分为两个步骤, 关键的函数有三个 configfs_create configfs_create_dirpopulate_attrs. .

1) configfs_create

    configfs_create 该接口用于创建 configfs 文件系统的的目录/文件/链接文件, 具体创建的是什么由 mode 中的标志位决定, 传入的 init 函数则用来设置对应目录/文件/链接文件的回调函数.

//fs/configfs/dir.c
int configfs_create(struct dentry * dentry, umode_t mode, void (*init)(struct inode *))
{
        int error = 0;
        struct inode *inode = NULL;
        struct configfs_dirent *sd;
        struct inode *p_inode;

        if (!dentry) // dentry 为空返回 err
                return -ENOENT;

        // 当前目录已存在返回 err
        if (d_really_is_positive(dentry))
                return -EEXIST;

        // 获取 dentry 对应的 configfs_dirent
        sd = dentry->d_fsdata;

        // 分配一个 inde, 初始化其 mode、sd 等
        inode = configfs_new_inode(mode, sd, dentry->d_sb);
        if (!inode)
                return -ENOMEM;

        
        p_inode = d_inode(dentry->d_parent); // 获取父目录的 inode
        p_inode->i_mtime = p_inode->i_ctime = CURRENT_TIME;  // 更新时间戳
        configfs_set_inode_lock_class(sd, inode);

        init(inode);  // 初始化 inode
        d_instantiate(dentry, inode);  // 将 inode 和 dentry 关联起来

        // 为目录或符号链接的 dentry 增加引用计数,防止被内核回收
        if (S_ISDIR(mode) || S_ISLNK(mode))
                dget(dentry);
        
        return error;
}
2) configfs_create_dir

    该接口用于在 configfs 文件系统中创建目录并设置读写权限, 其中 init_dir 接口用于设置底层回调接口.创建的目录具有的默认权限如下:

  • 用户 (User) 的权限,包含读、写、执行
  • 用户组 (Group) 和其他人 (Others) 对文件的读权限和执行权限
//fs/configfs/dir.c
static int configfs_create_dir(struct config_item *item, struct dentry *dentry)
{
        int error;
        // 核心代码设置
        // S_IFDIR: 表示当前inode 注册进内核后以目录的形式展现出来
        // S_IRWXU: 用户 (User) 的权限,包含读、写、执行
        // S_IRUGO: 用户组 (Group) 和其他人 (Others) 对文件的读权限
        // S_IXUGO: 用户组 (Group) 和其他人 (Others) 对文件的执行权限
        umode_t mode = S_IFDIR| S_IRWXU | S_IRUGO | S_IXUGO;
        struct dentry *p = dentry->d_parent; // 获取父目录的 dentry

        BUG_ON(!item);

        // 如果这个目录已经存在则返回 err
        error = configfs_dirent_exists(p->d_fsdata, dentry->d_name.name);
        if (unlikely(error))
                return error;

        // 创建 configfs_dirent 并初始化
        error = configfs_make_dirent(p->d_fsdata, dentry, item, mode,
                                     CONFIGFS_DIR | CONFIGFS_USET_CREATING);
        if (unlikely(error))
                return error;

        // 初始化默认的目录深度
        configfs_set_dir_dirent_depth(p->d_fsdata, dentry->d_fsdata);
        // 创建目录, 即调用 init_dir 设置目录的回调
        error = configfs_create(dentry, mode, init_dir);
        if (!error) {
                inc_nlink(d_inode(p));
                item->ci_dentry = dentry;
        } else {
                struct configfs_dirent *sd = dentry->d_fsdata;
                if (sd) {
                        spin_lock(&configfs_dirent_lock);
                        list_del_init(&sd->s_sibling);
                        spin_unlock(&configfs_dirent_lock);
                        configfs_put(sd);
                }
        }
        return error;
}

init_dir 接口用于设置目录操作的底层的回调接口.

static void init_dir(struct inode * inode)
{
        inode->i_op = &configfs_dir_inode_operations;
        inode->i_fop = &configfs_dir_operations;

        inc_nlink(inode);
}

// 在当前目录下的操作
const struct inode_operations configfs_dir_inode_operations = {
        .mkdir          = configfs_mkdir,   // 创建目录时的回调接口
        .rmdir          = configfs_rmdir,   // 删除目录时的回调接口
        .symlink        = configfs_symlink, // 创建链接文件的回调接口
        .unlink         = configfs_unlink,  // 删除链接文件的回调接口
        .lookup         = configfs_lookup,  // 这个接口是用来设置属性文件的 inode 接口回调函数的.
        .setattr        = configfs_setattr, // 设置附加属性回调接口
};

// 对当前目录的 open/read 等操作映射到这里
const struct file_operations configfs_dir_operations = {
        .open           = configfs_dir_open,   // 打开目录
        .release        = configfs_dir_close,  // 关闭目录
        .llseek         = configfs_dir_lseek,  // 改变文件偏移量(用于读取目录内容)
        .read           = generic_read_dir,    // 读取目录内容
        .iterate        = configfs_readdir,    // 遍历目录项
};

2. 编程实验

    这个实验很简单在内核中创建一个configfs 的根目录 myconfig.需要注意的是内核中只能有一个 configfs 文件系统, 所以要把原来默认的去掉.

// drivers/usb/Makefile
#obj-$(CONFIG_USB_GADGET)   += gadget/ //把这个注释掉 

编写代码如下

#include <linux/module.h>
#include <linux/configfs.h>

static struct config_item_type myconfig_type = { // 必须提供一个默认值
    .ct_owner = THIS_MODULE,
};

static struct configfs_subsystem myconfig_subsys = {
    .su_group = {
        .cg_item = {
            .ci_name = "myconfig", // 目录名
            .ci_type = &myconfig_type,
        },
    },
};

static int __init myconfig_init(void)
{
    int ret;

    config_group_init(&myconfig_subsys.su_group);
    mutex_init(&myconfig_subsys.su_mutex);

    // 注册一个 configfs 文件系统
    ret = configfs_register_subsystem(&myconfig_subsys);
    if (ret) {
        pr_err("Failed to register myconfig subsystem: %d\n", ret);
    } else {
        pr_info("myconfig subsystem registered successfully\n");
    }

    return ret;
}

static void __exit myconfig_exit(void)
{

    configfs_unregister_subsystem(&myconfig_subsys);
    pr_info("myconfig subsystem unregistered\n");
}

module_init(myconfig_init);
module_exit(myconfig_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("baron");
MODULE_DESCRIPTION("Simple ConfigFS Example: myconfig");

内核打印下面日志表示注册成功.

[    0.143302] myconfig subsystem registered successfully

mount 到 /sys/kernel/config路径

# mount -t configfs none /sys/kernel/config // 挂载到 /sys/kernel/config
#
# cd /sys/kernel/config/
# ls
myconfig // 注册的 configfs 文件系统.

3. populate_attrs

    这个函数的功能是遍历 config_item_type 中的 ct_attrs 数组, 为每一个属性文件分配 configfs_dirent 数据结构并初始化.

    configfs 中的属性文件和目录的创建是不一样的, 属性文件是不支持动态创建的, 需要提前想好, 当前目录下需要哪些属性文件, 然后提前放到 config_item_typect_attrs 数组里面.

    注意了这里并没有设置属性文件的 inode 回调接口, 这里设置了 CONFIGFS_ITEM_ATTR 标志位. 在 lookup 检测这个标志位, 如果设置了这个标志位, 则会设置 inode 的回调函数, 不是 mkdir 的 looup 回调, 这里提一下, 后文详述.

// fs/configfs/dir.c
static int populate_attrs(struct config_item *item)
{
        struct config_item_type *t = item->ci_type;
        struct configfs_attribute *attr;
        int error = 0;
        int i;

        if (!t)
                return -EINVAL;
        if (t->ct_attrs) { // 遍历 ct_attrs 数组, 为每一个属性文件分配 configfs_dirent 数据结构并初始化
                for (i = 0; (attr = t->ct_attrs[i]) != NULL; i++) {
                        if ((error = configfs_create_file(item, attr)))
                                break;
                }
        }

        if (error)
                detach_attrs(item);

        return error;
}

// 为属性文件分配 configfs_dirent 数据结构设置 CONFIGFS_ITEM_ATTR 标志位并初始化
int configfs_create_file(struct config_item * item, const struct configfs_attribute * attr)
{
        struct dentry *dir = item->ci_dentry;
        struct configfs_dirent *parent_sd = dir->d_fsdata;
        umode_t mode = (attr->ca_mode & S_IALLUGO) | S_IFREG;
        int error = 0;

        mutex_lock_nested(&d_inode(dir)->i_mutex, I_MUTEX_NORMAL);
        error = configfs_make_dirent(parent_sd, NULL, (void *) attr, mode, CONFIGFS_ITEM_ATTR);
        mutex_unlock(&d_inode(dir)->i_mutex);

        return error;
}

别忘了(后面会用):
configfs_attach_group = configfs_create_dir + populate_attrs

三、创建子目录

    configfs 的核心功能是配置, 也就是能够动态的配创建并置子项, 所谓的子项包括主要包括三个他们分别是目录/文件/链接文件.首先来看一下子目录的创建, 文件系统中通过 mkdir 来创建子目录, 我们在 configfs 文件系统中 mkdir 的流程如下所示.

在这里插入图片描述
    可以看出 mkdir 的过程会自动的帮助我们调用 configfs_attach_group 函数注册目录, 并且所属的属性文件分配 configfs_dirent 数据结构.我们只需要在 make_group 中填充我们需要的目录配置项就行了.优化前面的代码, 增加创建目录的功能吧.

#include <linux/module.h>
#include <linux/configfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/fs.h>

static struct config_group *myconfig_make_group(struct config_group *group, const char *name)
{
    struct config_group *new_group;

    pr_info("Creating new group: %s\n", name);

    /* 分配 config_group 内存 */
    new_group = kzalloc(sizeof(struct config_group), GFP_KERNEL);
    if (!new_group)
        return ERR_PTR(-ENOMEM);

    /* 初始化新组, 这里使用父目录的配置项, 通过这样的方式实现递归创建*/
    config_group_init_type_name(new_group, name, group->cg_item.ci_type);

    return new_group;
}

struct configfs_group_operations my_group_ops = {
    .make_group = myconfig_make_group,
};

static struct config_item_type myconfig_type = {
    .ct_owner = THIS_MODULE,
    .ct_group_ops = &my_group_ops,
};

static struct configfs_subsystem myconfig_subsys = {
    .su_group = {
        .cg_item = {
            .ci_namebuf = "myconfig",  // 目录名
            .ci_name = "myconfig",
            .ci_type = &myconfig_type,
        },
    },
};

static int __init myconfig_init(void)
{
    int ret;

    printk("%s\n", __func__);

    config_group_init(&myconfig_subsys.su_group);
    mutex_init(&myconfig_subsys.su_mutex);

    // 注册一个 configfs 文件系统
    ret = configfs_register_subsystem(&myconfig_subsys);
    if (ret) {
        pr_err("Failed to register myconfig subsystem: %d\n", ret);
    } else {
        pr_info("myconfig subsystem registered successfully\n");
    }

    return ret;
}

static void __exit myconfig_exit(void)
{

    configfs_unregister_subsystem(&myconfig_subsys);
    pr_info("myconfig subsystem unregistered\n");
}

module_init(myconfig_init);
module_exit(myconfig_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("baron");
MODULE_DESCRIPTION("Simple ConfigFS Example: myconfig");

验证结果:

# mount -t configfs none /sys/kernel/config/
# cd /sys/kernel/config/myconfig/
# ls
#
# mkdir dir
[   60.577645] configfs_mkdir make group: dir
[   60.581934] Creating new group: dir
#
# ls
dir
#
# cd dir/
#
# mkdir aaa
[  241.614193] configfs_mkdir make group: aaa
[  241.618429] Creating new group: aaa
# ls
aaa

四、创建属性文件

    前面提到过属性文件的创建是不能动态创建的, 需要提前想好当前目录下需要哪些属性文件, 然后提前放到 config_item_typect_attrs 数组中

    在 mkdir 的时候会调用自动 configfs_attach_group 接口, 这个接口中的 populate_attrs 会遍历config_item_typect_attrs 数组中每一个 attr, 为他们置了 CONFIGFS_ITEM_ATTR 标志位, 并且分配 configfs_dirent 数据结构.

    属性文件的底层回调接口是在读写的时候设置的, 在 open 文件的候, 会调用 configfs_lookup , 在这里设置属性文件的回调, 然后再 read/write 的的时候回调这里设置的回调接口. 流程如下图所示.

在这里插入图片描述

    由于 write 和 read 基本上是一样的就不给出流程了, 在 configfs_create 中会回调 configfs_init_file 设置属性接口的操作函数.

static void configfs_init_file(struct inode * inode)
{
        inode->i_size = PAGE_SIZE;
        inode->i_fop = &configfs_file_operations;
}

const struct file_operations configfs_file_operations = {
        .read           = configfs_read_file,
        .write          = configfs_write_file,
        .llseek         = generic_file_llseek,
        .open           = configfs_open_file,
        .release        = configfs_release,
};

    这整个流程都是自动进行的对于我们来说只需要填充数据结构然, 然后放到 ct_attrs 数组, 然后就会自动生成对应的属性文件, 读写的时候就会调用到对应的, show 和 store 接口.优化前面的接口添加属性文件吧.


#include <linux/module.h>
#include <linux/configfs.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/fs.h>

static struct config_group *myconfig_make_group(struct config_group *group, const char *name)
{
    struct config_group *new_group;

    pr_info("Creating new group: %s\n", name);

    /* 分配 config_group 内存 */
    new_group = kzalloc(sizeof(struct config_group), GFP_KERNEL);
    if (!new_group)
        return ERR_PTR(-ENOMEM);

    /* 初始化新组 */
    config_group_init_type_name(new_group, name, group->cg_item.ci_type);

    return new_group;
}

struct configfs_group_operations my_group_ops = {
    .make_group = myconfig_make_group,
};

static ssize_t test_show(struct config_item *item, char *buf)
{
    return snprintf(buf, PAGE_SIZE, "Hello, ConfigFS!\n");
}

// 写入 test 属性
static ssize_t test_store(struct config_item *item, const char *buf, size_t count)
{
    pr_info("test 属性被写入: %s", buf);
    return count;
}

// 定义 test 属性
static struct configfs_attribute test_attr = {
    .ca_name = "test",  // 属性名
    .ca_mode = S_IRUGO | S_IWUSR,  // 可读写权限
    .show = test_show,
    .store = test_store,
};

// 配置文件操作:显示和写入
static struct configfs_attribute *myconfig_attrs[] = {
    &test_attr,  // 将 test_attr 添加到属性数组
    NULL,  // 结束标志
};

static struct config_item_type myconfig_type = {
    .ct_owner = THIS_MODULE,
    .ct_group_ops = &my_group_ops,
    .ct_attrs = myconfig_attrs,  // 添加属性
};

static struct configfs_subsystem myconfig_subsys = {
    .su_group = {
        .cg_item = {
            .ci_namebuf = "myconfig",  // 目录名
            .ci_name = "myconfig",
            .ci_type = &myconfig_type,
        },
    },
};

static int __init myconfig_init(void)
{
    int ret;

    printk("%s\n", __func__);

    config_group_init(&myconfig_subsys.su_group);
    mutex_init(&myconfig_subsys.su_mutex);

    // 注册一个 configfs 文件系统
    ret = configfs_register_subsystem(&myconfig_subsys);
    if (ret) {
        pr_err("Failed to register myconfig subsystem: %d\n", ret);
    } else {
        pr_info("myconfig subsystem registered successfully\n");
    }

    return ret;
}

static void __exit myconfig_exit(void)
{

    configfs_unregister_subsystem(&myconfig_subsys);
    pr_info("myconfig subsystem unregistered\n");
}

module_init(myconfig_init);
module_exit(myconfig_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("baron");
MODULE_DESCRIPTION("Simple ConfigFS Example: myconfig");

验证结果:

# mount -t configfs none /sys/kernel/config/
#
#
# cd /sys/kernel/config/myconfig/
#
# ls
test
#
# echo 123 > test
[   44.786252] configfs_lookup: name:test // 这是我自己加的打印
[   44.793639] test 属性被写入: 123
#
# cat test
[   67.814740] configfs_lookup: name:test
Hello, ConfigFS!

相关文章:

  • DistilQwen2.5发布:通义千问蒸馏小模型再升级
  • Grafana使用日志4--直接对接Zabbix数据库的一些注意点
  • DeepSeek-R1技术全解析:如何以十分之一成本实现OpenAI级性能?
  • FFmpeg.NET:.NET 平台上的音视频处理利器
  • PDF转HTML 超级好用 免费在线转换PDF 完美转换格式
  • [特殊字符]《封印adb的黑暗通道:让系统文件成为魔法禁书区的终极指南》[特殊字符]
  • DeepSeek 部署实战:Ollama + 多客户端 + RAG
  • 【数三角——枚举,哈希】
  • OceanBase + DeepSeek:5分钟免费搭建企业知识库
  • K8s部署主从结构MySQL服务
  • C++的三种对象模型
  • 项目访问使用 docker bridge 网络模式(端口映射)配置详解
  • 全域旅游景区导览系统:赋能智慧旅游生态,破解行业核心难题
  • 外发抄板SCH与PCB检查系列
  • React七Formik
  • 2025.2.25学习内容
  • 【python随手记】——读取文本文件内容转换为json格式
  • 【机器学习】梯度下降法及使用一元二次方程模拟使用梯度下降法的代码实现
  • 45.matlab产生正弦叠加信号
  • 系统运维方案,系统运维的主要工作内容和运维检查单等资料,设备监控,设备运维检查(Word)
  • 郑州网站建设选智巢/成都全网推广哪家专业
  • 邮轮哪个网站是可以做特价胃肠的/怎么做一个免费的网站
  • 免费网站托管/百度网盘客户端
  • 有什么做数学题的网站/seo职位
  • 做网站公司流程/seo页面优化公司
  • 舆情报告单/seowhy论坛