【Linux kernel 文件系统入门及渐进 3 -- sysfs 与 kernfs】

1.1 kernfs 引入背景

kernfs 是 kernel 3.14 引入的内存文件系统。在Linux内核中,kernfs 提供内核子系统内部伪文件系统所需的功能,源于拆分 sysfs 使用的部分内部逻辑,它通过将有关硬件设备和相关设备驱动程序的信息从内核的设备模型导出到用户空间,提供一组虚拟文件,从而实现独立且可重用的功能。 其他内核子系统可以更容易,更一致地实现自己的伪文件系统。

kernfs_node 表示 kernfs 层次的构成部分(building block),每个 kernfs 节点由单个 kernfs_node 表示。大多数字段都是 kernfs 专用的,不应该由 kernfs 用户直接访问。

初始化的时候创建 kernfs_node_cache 的 cache,只有在函数 __kernfs_new_node 中使用。
有两个函数会调用:
1)kernfs_new_node;
2)sysfs_init 通过 kernfs_create_root(struct kernfs_syscall_ops *scops, unsigned int flags, void *priv) 一个新的 kernfs 层次,然后将其保存在静态全局变量 sysfs_root 中,供各处使用。然后通过 register_filesystem 将其注册为名为 sysfs 的文件系统。

int __init sysfs_init(void)
{
	int err;
 
	sysfs_root = kernfs_create_root(NULL, 
									KERNFS_ROOT_EXTRA_OPEN_PERM_CHECK,
									NULL);
	if (IS_ERR(sysfs_root))
		return PTR_ERR(sysfs_root);
 
	sysfs_root_kn = sysfs_root->kn;
 
	err = register_filesystem(&sysfs_fs_type);
	if (err) {
		kernfs_destroy_root(sysfs_root);
		return err;
	}
 
	return 0;
}

这时,我们看以看到一个完整的目录层次结构(sys目录下有10个子目录,子目录下又有各个子目录),以及这个目录结构的创建方式。

# ls /sys
block     class     devices   fs        module
bus       dev       firmware  kernel    power

在系统中,通过 kobject_create_and_addkset_create_and_add 创建这10个目录的逻辑结构,在这个逻辑层次它们都没有父节点,它们的 parent_kobj 参数都为空:

fs_kobj = kobject_create_and_add("fs", NULL);
power_kobj = kobject_create_and_add("power", NULL);
kernel_kobj = kobject_create_and_add("kernel", NULL);
dev_kobj = kobject_create_and_add("dev", NULL);
firmware_kobj = kobject_create_and_add("firmware", NULL);
block_depr = kobject_create_and_add("block", NULL);
 
devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
class_kset = kset_create_and_add("class", NULL, NULL);
module_kset = kset_create_and_add("module", &module_uevent_ops, NULL);
bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);

kset_create_and_add 函数最终都调用到了相同的函数 kobject_add_internal, 在这个函数中最终会调用到 sysfs_create_dir_ns,创建 kernfs_node 保存到 struct kobjectsd 字段中:

int sysfs_create_dir_ns(struct kobject *kobj, const void *ns)
{
	struct kernfs_node *parent, *kn;
 
	BUG_ON(!kobj);
 
	if (kobj->parent) 
		parent = kobj->parent->sd;
	else //sys目录下的10个子目录这个逻辑层次的kobj时,parent都设置为空
		parent = sysfs_root_kn;
 
	if (!parent)
		return -ENOENT;
 
	kn = kernfs_create_dir_ns(parent, kobject_name(kobj),
				  S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns);
	if (IS_ERR(kn)) {
		if (PTR_ERR(kn) == -EEXIST)
			sysfs_warn_dup(parent, kobject_name(kobj));
		return PTR_ERR(kn);
	}
 
	kobj->sd = kn;
	return 0;
}

在代码中可以看到:if (kobj->parent),如果为空,在 kernfs_node 这个逻辑层次的父节点,设置为:sysfs_root_kn

1.1.1 目录映射

如上文所述 kobject 在 sysfs 中对应的是目录(dir)。当我们注册一个 kobject 时,会调用 kobject_add 。于是:

kobject_add 
	=> kobject_add_varg 
		=> kobject_add_internal 
			=> create_dir 
				=> sysfs_create_dir_ns

如果 kobj 有 parent ,则它的父节点为 kobj->parent->sd ,否则为根目录节点 sysfs_root_kn 。于是将其作为参数调用 kernfs_create_dir_ns(parent, kobject_name(kobj), S_IRWXU | S_IRUGO | S_IXUGO, kobj, ns); 在父节点目录下创建一个名为 kobj->name 的目录。

1.1.2 属性映射

属性” 在 sysfs 中对应的是文件(file)。当需要为设备添加属性时,可以调用 device_create_file ,于是:

device_create_file => sysfs_create_file => sysfs_create_file_ns => sysfs_add_file_mode_ns => __kernfs_create_file属性创建文件,并根据 kobj->ktype->sysfs_ops 为文件绑定对应的读写函数。

创建的文件大小即为存放该属性值的长度,对于普通属性来说,大小为 PAGE_SIZE(4K),而对于二进制属性来说,大小由属性自定义,即 bin_attribute.size 指定。

当用户对属性文件进行读写时,会调用绑定的读写函数,比如对于 mode 为 SYSFS_PREALLOC 且 kobj->ktype->sysfs_ops 定义了 show 和 store 函数的属性,绑定是 sysfs_prealloc_kfops_rw 。这里的 kobj 指的是该属性的父节点也就是属性所属设备的 kobj

于是在读文件时,调用 sysfs_kf_read它会根据属性文件找到其父节点类型对应的 sysfs_ops ,然后调用 sysfs_ops.show 。show 需要将输出写到传入的 buf 缓冲区中,并返回写入的长度。

在写文件时,调用 sysfs_kf_write ,它会根据属性文件找到其父节点类型对应的 sysfs_ops ,然后调用 sysfs_ops.store 。 store 可以从传入的 buf 缓冲区中,读取用户写入的长度为 len 的内容。

但是需要注意的是 sysfs_ops 中的 show 和 store 函数并非是读写我们属性所需要的 show 和 store。因为一个设备只有一个类型,因此 sysfs_ops 到 show 和 store 只有一种实现,但实际上 show 和 store 应该根据属性的不同而不同。怎么办呢?绕个弯子:在调用 sysfs_ops.show 和 sysfs_ops.store 时传入属性 attribute 的指针,然后在函数中将指针转换为设备类型对应属性的指针后调用属性的 show 和 store 函数。这也就是 device_attributeclass_attribute 或一些设备自定义属性(比如 cpuidle_driver_attr) 中定义有 show 和 store 函数的原因。

1.2 sysfs 在设备模型中的应用

让我们通过设备模型 class.c 中有关 sysfs 的实现,来总结一下 sysfs 的应用方式。
首先,在 class.c 中,定义了 Class 所需的 ktype 以及 sysfs_ops 类型的变量,如下:

 1: /* kernel 4.14: drivers/base/class.c, line 74 */
 2: static const struct sysfs_ops class_sysfs_ops = {
 3:     .show      = class_attr_show,
 4:     .store     = class_attr_store,
 5:     .namespace = class_attr_namespace,
 6: };  
 7: 
 8: static struct kobj_type class_ktype = {
 9:     .sysfs_ops  = &class_sysfs_ops,
 10:    .release    = class_release,
 11:    .child_ns_type  = class_child_ns_type,
 12: };

如文章【Linux kernel 文件系统入门及渐进 1 – sysfs 介绍】可知,所有 class_typeKobject(/sys/class/) 下面的 attribute 文件的读写操作,都会交给 class_attr_showclass_attr_store 两个接口处理。以 class_attr_show 为例:

/* drivers/base/class.c, line 24 */
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr) 

static ssize_t class_attr_show(struct kobject *kobj, 
							   struct attribute *attr, char *buf)
{   
     struct class_attribute *class_attr = to_class_attr(attr);--->(1)
     struct subsys_private *cp = to_subsys_private(kobj); ------->(2)
     ssize_t ret = -EIO;
  
    if (class_attr->show)
    	ret = class_attr->show(cp->class, class_attr, buf);
    return ret;
}

(1) 找出包含这个 attr 的 struct class_attribute * 指针, 使用 container_ofstruct attribute 类型的指针中取得一个 class 模块的自定义指针:struct class_attribute,该指针中包含了class 模块自身的 show 和 store 接口。

(2) 找出包含这个 kobj 的 struct class * 指针,struct class 没有直接包含 kobj,通过subsys.kobj 间接找到 struct class * 指针。

下面是 struct class_attribute 的声明:

  /* include/linux/device.h, line 399 */
  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);
      const void *(*namespace)(struct class *class, const struct class_attribute *attr); 
  };

因此,所有需要使用 attribute 的模块,都不会直接定义 struct attribute 变量,而是通过一个自定义的数据结构,该数据结构的一个成员是 struct attribute 类型的变量,并提供 showstore 回调函数。然后在该模块 ktype 所对应的 struct sysfs_ops 变量中,实现该本模块整体的 showstore 函数,并在被调用时,转接到自定义数据结构(struct class_attribute)中的 showstore 函数中。这样,每个 atrribute 文件,实际上对应到一个自定义数据结构变量中了。

参考:
http://www.wowotech.net/device_model/dm_sysfs.html
https://blog.csdn.net/zhoudawei/article/details/86669868

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

主公CodingCos

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值