1. 编译运行

代码从如下链接获得:

https://github.com/torvalds/linux/blob/master/samples/configfs/configfs_sample.c

编写 Makefile 文件:

obj-m += configfs_sample.o
all:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
make -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean

编译生成内核模块:

make
ls -l

-rwxr--r-- 1 abin abin 10K Oct 27 16:58 configfs_sample.c

-rw-rw-r-- 1 abin abin 13K Oct 29 11:16 configfs_sample.ko

-rw-rw-r-- 1 abin abin 603 Oct 29 11:16 configfs_sample.mod.c

-rw-rw-r-- 1 abin abin 2.6K Oct 29 11:16 configfs_sample.mod.o

-rw-rw-r-- 1 abin abin 12K Oct 29 11:16 configfs_sample.o

-rw-rw-r-- 1 abin abin 166 Oct 29 11:16 Makefile

-rw-rw-r-- 1 abin abin 92 Oct 29 11:16 modules.order

-rw-rw-r-- 1 abin abin 0 Oct 29 11:16 Module.symvers

其中,configfs_sample.ko 使编译好的内核模块,使用如下命令加载该模块:

sudo modprobe configfs_sample.ko

如果出现如下错误:

modprobe: FATAL: Module configfs_sample.ko not found in directory /lib/modules/4.15.0-117-generic

将 configfs_sample.ko 拷贝进 /lib/modules/4.15.0-117-generic 再次尝试。

查看 configfs_sample.ko 内核模块是否已经挂载:

lsmod  | grep configfs_sample

configfs_sample 16384 0

查看 configfs 根目录:

ls -l /sys/kernel/config/

total 0

drwxr-xr-x 2 root root 0 Oct 29 11:32 01-childless

drwxr-xr-x 2 root root 0 Oct 29 11:32 02-simple-children

drwxr-xr-x 2 root root 0 Oct 29 11:32 03-group-children

如需卸载模块,使用如下命令:

sudo modprobe -r configfs_sample.ko

2. 代码理解

为了理解代码,我们首先整理一下 configfs 中的层级结构:

内核模块初始化入口:

module_init(configfs_example_init);

configfs_example_init(void) 函数:

/*
* 此处是configfs_subsystem结构体数组,分别对应示例中的三个configfs子系统
*/
static struct configfs_subsystem *example_subsys[] = {
&childless_subsys.subsys,
&simple_children_subsys,
&group_children_subsys,
NULL,
}; static int __init configfs_example_init(void)
{
int ret;
int i;
struct configfs_subsystem *subsys; //configfs子系统 for (i = 0; example_subsys[i]; i++) {
subsys = example_subsys[i]; config_group_init(&subsys->su_group); //初始化 group
mutex_init(&subsys->su_mutex); //初始化 mutex
ret = configfs_register_subsystem(subsys); //注册 subsystem
if (ret) {
printk(KERN_ERR "Error %d while registering subsystem %s\n", ret,
subsys->su_group.cg_item.ci_namebuf);
goto out_unregister;
}
} return 0; out_unregister:
for (i--; i >= 0; i--)
configfs_unregister_subsystem(example_subsys[i]); return ret;
}

程序的主要逻辑是通过 struct configfs_subsystem 结构体传递给 configfs 的,下面分别对3个示例进行分析。

2.1 示例01-childless

变量 childless_subsys 的内容:

struct childless {
struct configfs_subsystem subsys;
int showme;
int storeme;
}; static struct childless childless_subsys = {
.subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "01-childless",
.ci_type = &childless_type, //struct config_item_type,定义操作、属性等
},
},
},
};

childless_type 变量如下:

static const struct config_item_type childless_type = {
.ct_attrs = childless_attrs, //configfs_attribute,只定义了属性,没有定义对item和group操作
.ct_owner = THIS_MODULE,
};

childless_attrs 是一个数组,以 NULL 结尾。以下定义了三个属性,在 configfs 中,将表现为3个文件:

static struct configfs_attribute *childless_attrs[] = {
&childless_attr_showme,
&childless_attr_storeme,
&childless_attr_description,
NULL,
};

childless_attr_showme,childless_attr_storeme 和 childless_attr_description 三个属性是通过以下函数创建的:

CONFIGFS_ATTR_RO(childless_, showme);	//需要定义childless_showme_show()函数
CONFIGFS_ATTR(childless_, storeme); //需要定义childless_storeme_show()和childless_storeme_store()函数
CONFIGFS_ATTR_RO(childless_, description); //需要定义childless_description_show()函数

创建属性的函数有3个,在 linux/configfs.h 中:

#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, \
} #define CONFIGFS_ATTR_RO(_pfx, _name) \
static struct configfs_attribute _pfx##attr_##_name = { \
.ca_name = __stringify(_name), \
.ca_mode = S_IRUGO, \
.ca_owner = THIS_MODULE, \
.show = _pfx##_name##_show, \
} #define CONFIGFS_ATTR_WO(_pfx, _name) \
static struct configfs_attribute _pfx##attr_##_name = { \
.ca_name = __stringify(_name), \
.ca_mode = S_IWUSR, \
.ca_owner = THIS_MODULE, \
.store = _pfx##_name##_store, \
}

可以看到,这三个宏定义函数可以根据传入的参数定义不同的结构体变量,变量名为:_pfx_attr_name,同时也会定义相应的 show 和 store 函数名。

CONFIGFS_ATTR(_pfx, _name) 需要定义 show 和 store 函数,相应的函数名分别为:_pfx_name_show 和 _pfx_name_store;

CONFIGFS_ATTR_RO(_pfx, _name)只需要定义 show 函数;

CONFIGFS_ATTR_WO(_pfx, _name) 只需要定义 store 函数。

childless_showme_show(),childless_storeme_show(),childless_storeme_store()和childless_description_show()的定义如下:

/*
* 传入item,得到该item所在的childless结构体
*/
static inline struct childless *to_childless(struct config_item *item)
{
return item ? container_of(to_configfs_subsystem(to_config_group(item)), struct childless, subsys) : NULL;
} //childless_showme_show函数的实现,根据item找到结构体struct childless,输出childless->showme,然后将childless->showme加1
static ssize_t childless_showme_show(struct config_item *item, char *page)
{
struct childless *childless = to_childless(item);
ssize_t pos; pos = sprintf(page, "%d\n", childless->showme);
childless->showme++; return pos;
}
//childless_storeme_show函数实现,输出结构体struct childless成员storeme的值
static ssize_t childless_storeme_show(struct config_item *item, char *page)
{
return sprintf(page, "%d\n", to_childless(item)->storeme);
}
//childless_storeme_store函数实现,接受从文件系统输入的值,保存在struct childless成员storeme中
static ssize_t childless_storeme_store(struct config_item *item, const char *page, size_t count)
{
struct childless *childless = to_childless(item);
unsigned long tmp;
char *p = (char *) page; tmp = simple_strtoul(p, &p, 10); //将字符串转化为10进制数字,类型为unsigned long
if (!p || (*p && (*p != '\n')))
return -EINVAL; if (tmp > INT_MAX)
return -ERANGE; childless->storeme = tmp; return count;
}
//childless_description_show函数实现,向page中填充内容
static ssize_t childless_description_show(struct config_item *item, char *page)
{
return sprintf(page,
"[01-childless]\n"
"\n"
"The childless subsystem is the simplest possible subsystem in\n"
"configfs. It does not support the creation of child config_items.\n"
"It only has a few attributes. In fact, it isn't much different\n"
"than a directory in /proc.\n");
}

根据我的理解,page指向一块内存空间,这块空间接收来自文件系统的数据,同时,负责将configfs中的内容输出给文件系统。

showme 文件运行效果:

cat showme

1

cat showme

2

storeme 文件运行效果:

cat storeme

0

echo 1111 > storeme
cat storeme

1111

2.2 示例02-simple-children

simple_children_subsys 变量的内容:

struct simple_children {
struct config_group group;
}; static struct configfs_subsystem simple_children_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "02-simple-children",
.ci_type = &simple_children_type,
},
},
};

simple_children_type 内容:

static const struct config_item_type simple_children_type = {
.ct_item_ops = &simple_children_item_ops, //item的操作
.ct_group_ops = &simple_children_group_ops, //group的操作
.ct_attrs = simple_children_attrs, //属性,和01相同
.ct_owner = THIS_MODULE,
};

可以看到,与01示例相比,02-siimple-chiildren 不光定义了 ct_attrs,还定义了 ct_item_ops 和 ct_group_ops。先看看赋值给 ct_attrs 的变量 simple_children_attrs:

static struct configfs_attribute *simple_children_attrs[] = {
&simple_children_attr_description, //属性,configfs中表示为文件
NULL,
};

simple_children_attrs 定义对象的属性,在 configfs 中表示为文件。simple_children_attr_description 是通过宏函数创建的:

CONFIGFS_ATTR_RO(simple_children_, description);

在 CONFIGFS_ATTR_RO 宏函数中会使用 show 函数,定义如下:

static ssize_t simple_children_description_show(struct config_item *item, char *page)
{
return sprintf(page,
"[02-simple-children]\n"
"\n"
"This subsystem allows the creation of child config_items. These\n"
"items have only one attribute that is readable and writeable.\n");
}

此处和01示例没什么区别,主要看 ct_item_ops 和 ct_group_ops。simple_children_item_ops 的定义如下:

static struct configfs_item_operations simple_children_item_ops = {
.release = simple_children_release, //实现release函数
};

simple_children_item_ops 是 struct configfs_item_operations 类型,也很简单,只定义了 release 函数,simple_children_release 函数定义如下:

static void simple_children_release(struct config_item *item)
{
kfree(to_simple_children(item)); //将item转换为simple_children结构体并释放内核分配的内存
}

simple_children_group_ops 的定义如下:

static struct configfs_group_operations simple_children_group_ops = {
.make_item = simple_children_make_item,
};

simple_children_group_ops 也很简单,只实现了 make_item 函数。simple_children_make_item 如下:

/*
* 传入item,得到该item所在的simple_children结构体
*/
static inline struct simple_children *to_simple_children(struct config_item *item)
{
return item ? container_of(to_config_group(item), struct simple_children, group) : NULL;
} static struct config_item *simple_children_make_item(struct config_group *group, const char *name)
{
struct simple_child *simple_child; simple_child = kzalloc(sizeof(struct simple_child), GFP_KERNEL); //为simple_child分配内存
if (!simple_child)
return ERR_PTR(-ENOMEM); config_item_init_type_name(&simple_child->item, name, &simple_child_type); //创建新的item时,使用config_item_init_type_name初始化,simple_child_type是子item使用的config_item_type结构体 simple_child->storeme = 0; //将simple_child的storeme设置为0 return &simple_child->item;
}

simple_child_type定义如下:

struct simple_child {
struct config_item item;
int storeme;
}; static const struct config_item_type simple_child_type = {
.ct_item_ops = &simple_child_item_ops,
.ct_attrs = simple_child_attrs,
.ct_owner = THIS_MODULE,
};

同上,定义了 ct_attrs 和 ct_item_ops,没有定义 ct_item_ops,simple_child_attrs变量定义如下:

CONFIGFS_ATTR(simple_child_, storeme);

static struct configfs_attribute *simple_child_attrs[] = {
&simple_child_attr_storeme,
NULL,
};

需要定义 show 和 store 函数:

static inline struct simple_child *to_simple_child(struct config_item *item)
{
return item ? container_of(item, struct simple_child, item) : NULL;
} /*
* 子item的show函数,将item转换为simple_child结构体并输出storeme的值
*/
static ssize_t simple_child_storeme_show(struct config_item *item, char *page)
{
return sprintf(page, "%d\n", to_simple_child(item)->storeme);
} /*
* 子item的store函数,将从文件系统输入的值保存在simple_child->storeme中
*/
static ssize_t simple_child_storeme_store(struct config_item *item, const char *page, size_t count)
{
struct simple_child *simple_child = to_simple_child(item);
unsigned long tmp;
char *p = (char *) page; tmp = simple_strtoul(p, &p, 10); //将字符串转换为10进制数字
if (!p || (*p && (*p != '\n')))
return -EINVAL; if (tmp > INT_MAX)
return -ERANGE; simple_child->storeme = tmp; return count;
}

运行效果:

make child
ls

child description

cd child
ls -l

total 0

-rw-r--r-- 1 root root 4096 Nov 3 21:57 storeme

child 文件夹中的 store 文件是自动创建的,这是因为定义了 make_item 函数,初始化 item 时 simple_child_type 变量的作用。

cat storeme

0

echo 2222 > storeme
cat storeme

2222

2.3 示例03-group-children

group_children_subsys变量的内容为:

static struct configfs_subsystem group_children_subsys = {
.su_group = {
.cg_item = {
.ci_namebuf = "03-group-children",
.ci_type = &group_children_type,
},
},
};

group_children_type 的定义如下:

static const struct config_item_type group_children_type = {
.ct_group_ops = &group_children_group_ops,
.ct_attrs = group_children_attrs,
.ct_owner = THIS_MODULE,
};

可以看到,和示例02相比,此处没有定义对 item 的操作,只定义了对 group 的操作。先看 group_children_attrs 的定义:

CONFIGFS_ATTR_RO(group_children_, description);

static struct configfs_attribute *group_children_attrs[] = {
&group_children_attr_description,
NULL,
};

同样,使用 CONFIGFS_ATTR_RO 宏定义函数需要先定义好 show 函数:

static ssize_t group_children_description_show(struct config_item *item, char *page)
{
return sprintf(page,
"[03-group-children]\n"
"\n"
"This subsystem allows the creation of child config_groups. These\n"
"groups are like the subsystem simple-children.\n");
}

此处和示例01和02都一样,下面是示例03的重点:

static struct config_group *group_children_make_group( struct config_group *group, const char *name)
{
struct simple_children *simple_children; //此处使用的struct simple_children结构体是示例02中定义的结构体 simple_children = kzalloc(sizeof(struct simple_children), GFP_KERNEL); //分配内存
if (!simple_children)
return ERR_PTR(-ENOMEM); config_group_init_type_name(&simple_children->group, name, &simple_children_type); //初始化group,simple_children_type也是示例02中定义的 return &simple_children->group;
}

这段代码负责创建group,初始化group时使用 simple_children_type变量,该变量是 struct config_item_type 类型,其中定义的内容就是实例02的内容。

在configfs中的表现为:在 03-group-children 下创建的每个目录,都相当于加载内核模快时创建的 02-simple-children 目录。

运行效果:

mkdir group
ls

description group

cd group
ls -l

total 0

-r--r--r-- 1 root root 4096 Nov 3 22:20 description

cat description

[02-simple-children]

This subsystem allows the creation of child config_items. These

items have only one attribute that is readable and writeable.

到这里可以看到,在示例03创建的目录等同于 02-simple-children 目录,下面的操作的示例02效果一样。

mkdir group_child
ls

description group_child

cd group_child
ls

storeme

cat storeme

0

echo 3333 > storeme
cat storeme

3333

configfs_sample.c 理解的更多相关文章

  1. 理解CSS视觉格式化

    前面的话   CSS视觉格式化这个词可能比较陌生,但说起盒模型可能就恍然大悟了.实际上,盒模型只是CSS视觉格式化的一部分.视觉格式化分为块级和行内两种处理方式.理解视觉格式化,可以确定得到的效果是应 ...

  2. 彻底理解AC多模式匹配算法

    (本文尤其适合遍览网上的讲解而仍百思不得姐的同学) 一.原理 AC自动机首先将模式组记录为Trie字典树的形式,以节点表示不同状态,边上标以字母表中的字符,表示状态的转移.根节点状态记为0状态,表示起 ...

  3. 理解加密算法(三)——创建CA机构,签发证书并开始TLS通信

    接理解加密算法(一)--加密算法分类.理解加密算法(二)--TLS/SSL 1 不安全的TCP通信 普通的TCP通信数据是明文传输的,所以存在数据泄露和被篡改的风险,我们可以写一段测试代码试验一下. ...

  4. node.js学习(三)简单的node程序&&模块简单使用&&commonJS规范&&深入理解模块原理

    一.一个简单的node程序 1.新建一个txt文件 2.修改后缀 修改之后会弹出这个,点击"是" 3.运行test.js 源文件 使用node.js运行之后的. 如果该路径下没有该 ...

  5. 如何一步一步用DDD设计一个电商网站(一)—— 先理解核心概念

    一.前言     DDD(领域驱动设计)的一些介绍网上资料很多,这里就不继续描述了.自己使用领域驱动设计摸滚打爬也有2年多的时间,出于对知识的总结和分享,也是对自我理解的一个公开检验,介于博客园这个平 ...

  6. 学习AOP之透过Spring的Ioc理解Advisor

    花了几天时间来学习Spring,突然明白一个问题,就是看书不能让人理解Spring,一方面要结合使用场景,另一方面要阅读源代码,这种方式理解起来事半功倍.那看书有什么用呢?主要还是扩展视野,毕竟书是别 ...

  7. ThreadLocal简单理解

    在java开源项目的代码中看到一个类里ThreadLocal的属性: private static ThreadLocal<Boolean> clientMode = new Thread ...

  8. JS核心系列:理解 new 的运行机制

    和其他高级语言一样 javascript 中也有 new 运算符,我们知道 new 运算符是用来实例化一个类,从而在内存中分配一个实例对象. 但在 javascript 中,万物皆对象,为什么还要通过 ...

  9. 深入理解JS 执行细节

    javascript从定义到执行,JS引擎在实现层做了很多初始化工作,因此在学习JS引擎工作机制之前,我们需要引入几个相关的概念:执行环境栈.全局对象.执行环境.变量对象.活动对象.作用域和作用域链等 ...

随机推荐

  1. 适用于 deno 的多版本管理工具 dvm 发布

    不知不觉中,deno 已经默默的发布了 3 个版本了: 0.1.0 0.1.1 0.1.2 昨晚通宵做了一个 deno 多版本的管理工具: dvm. github 地址: https://github ...

  2. log4net 纯代码配置

    当需要输出的日志很多的时候,每次修改config都很麻烦,于是想可不可以动态生成. 网上找的案例都是获取单个appender/logger的,此处例子是任意logger,appender相同 log4 ...

  3. Redis 4.0.2分布式锁的Java实现

    简介 Redis分布式锁算法有两种,一种是单个Redis实例下的,一种是多个Redis实例的Redlock算法. 官方推荐Redlock算法,但是这个算法需要比较多的Redis实例而且是完全互相独立, ...

  4. MDK内的KEEP关键字以及$$Base $$Limit

    使用mdk编程,假如有一个有用的函数你定义了但是没有显式的调用,mdk在默认方式下,将会把这个函数从整个程序总删除掉,以节省ROM. 比如,你在ROM的0x00002000处定位了一个函数,假设为vo ...

  5. python中def用法

    转载:https://blog.csdn.net/qq_21466543/article/details/81604826 一.函数调用的含义 函数是类似于可封装的程序片段.允许你给一块语句一个名字, ...

  6. Java 使用UDP传输一个小文本文件

    工具1:Eclipse 工具2:IntelliJ IDEA Java工程的目录结构(基于IntelliJ IDEA) 例1.1:接收方,因为接收到的数据是字节流,为了方便,这里是基于Apache co ...

  7. 步进电机的Arduino库函数

    This library allows you to control unipolar or bipolar stepper motors. To use it you will need a ste ...

  8. Docker笔记2:Docker 镜像仓库

    Docker 镜像的官方仓库位于国外服务器上,在国内下载时比较慢,但是可以使用国内镜像市场的加速器(比如阿里云加速器)以提高拉取速度. Docker 官方的镜像市场,可以和 Gitlab 或 GitH ...

  9. devops-jenkins-Pipeline基础语法

    1. jenkins-Pipeline基础语法  1) jenkins-Pipeline总体介绍 • Pipeline,简而言之,就是一套运行与jenkins上的工作流框架,将原本独立运行于单个或多个 ...

  10. 怎么快速从产品助理/初级 PM 成长为高级 PM?

    一般想成为一枚产品经理的同学,如果没有经过系统的学习,都是从产品专员/助理开始做起的~ 那要想快速从产品助理/初级 PM 成长为高级 PM,以下这几点必不可少 直接上干货~ 全文篇幅较长,可以点赞收藏 ...