乐趣区

关于linux-kernel:configfssamplec-理解

转载:https://www.cnblogs.com/sctb/…

1. 编译运行

代码从如下链接取得:

https://github.com/torvalds/l…

编写 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
退出移动版