LV100-属性文件简介
一、属性文件是什么?
1. 属性文件简介
属性(attribute)文件就是就是内核空间和用户空间进行信息交互的一种方法。它是对应 kobject 而言的,指的是 kobject 的“属性”。
我们知道每一个注册的 kobject 都会在 /sys 中以目录的形式呈现,也就是 bus 等数据结构可以利用嵌入 kobject 可以使它显示在 /sys 中。而 attribute 又会以文件的形式出现在目录中, 即 kobject 的所有属性,都在它对应的 sysfs 目录下以文件的形式呈现。通过这些属性文件, 我们就能够获取/修改内核中的变量,字符串等信息。
例如某个 driver 中定义了一个变量,希望用户空间程序可以修改该变量,以控制 driver 的运行行为,那么就可以将该变量以 sysfs attribute 的形式开放出来。
Linux 内核中,attribute 分为普通的 attribute 和二进制 attribute。
2. 数据结构
会接触到的属性相关的结构体大概有以下几个。
2.1 struct attribute
struct attribute 定义如下:
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};其中 name 表示文件名称,mode 表示文件模式(也就是权限,比如 0644、0666 等)。其他几个是调试用的。
2.2 struct bin_attribute
struct bin_attribute 定义如下:
struct bin_attribute {
struct attribute attr;
size_t size;
void *private;
ssize_t (*read)(struct file *, struct kobject *, struct bin_attribute *,
char *, loff_t, size_t);
ssize_t (*write)(struct file *, struct kobject *, struct bin_attribute *,
char *, loff_t, size_t);
int (*mmap)(struct file *, struct kobject *, struct bin_attribute *attr,
struct vm_area_struct *vma);
};2.3 struct attribute_group
struct attribute_group 定义如下:
struct attribute_group {
const char *name;
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
umode_t (*is_bin_visible)(struct kobject *,
struct bin_attribute *, int);
struct attribute **attrs;
struct bin_attribute **bin_attrs;
};2.4 struct kobj_attribute
struct kobj_attribute 定义如下:
struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
};这个结构体包含一个 attr 成员,它就是前面的属性结构体,后面的 show 和 store 函数指针就是为每个属性指定不同的回调函数的。
3. 相关 API
3.1 sysfs_create_file()
sysfs_create_file() 函数定义如下:
static inline int __must_check sysfs_create_file(struct kobject *kobj,
const struct attribute *attr)
{
return sysfs_create_file_ns(kobj, attr, NULL);
}3.2 sysfs_create_files()
sysfs_create_files() 函数定义如下:
int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
{
int err = 0;
int i;
for (i = 0; ptr[i] && !err; i++)
err = sysfs_create_file(kobj, ptr[i]);
if (err)
while (--i >= 0)
sysfs_remove_file(kobj, ptr[i]);
return err;
}
EXPORT_SYMBOL_GPL(sysfs_create_files);3.3 sysfs_remove_file()
sysfs_remove_file() 函数定义如下:
static inline void sysfs_remove_file(struct kobject *kobj,
const struct attribute *attr)
{
sysfs_remove_file_ns(kobj, attr, NULL);
}3.4 sysfs_remove_files()
sysfs_remove_files() 函数定义如下:
int sysfs_create_files(struct kobject *kobj, const struct attribute **ptr)
{
int err = 0;
int i;
for (i = 0; ptr[i] && !err; i++)
err = sysfs_create_file(kobj, ptr[i]);
if (err)
while (--i >= 0)
sysfs_remove_file(kobj, ptr[i]);
return err;
}
EXPORT_SYMBOL_GPL(sysfs_create_files);3.5 sysfs_create_group()
sysfs_create_group() 函数定义如下:
int sysfs_create_group(struct kobject *kobj,
const struct attribute_group *grp)
{
return internal_create_group(kobj, 0, grp);
}
EXPORT_SYMBOL_GPL(sysfs_create_group);用于在 sysfs 中创建一个组(group)。组是一组相关的属性文件的集合,可以将它们放在同一个目录下提供更好的组织性和可读性。此函数有俩个参数,分别为如下所示:
- kobj: 指向包含目标组的 kobject 的指针。
- grp: 指向 struct attribute_group 结构体的指针,该结构体定义了组中的属性文件。
struct attribute_group 定义如下:
struct attribute_group {
// 组的名称,将用作 sysfs 目录的名称。
const char *name;
// 可选的回调函数,用于决定每个属性文件是否可见。如果为 NULL,则所有属性文件都可见。
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
umode_t (*is_bin_visible)(struct kobject *,
struct bin_attribute *, int);
// 指向属性文件数组的指针
struct attribute **attrs;
struct bin_attribute **bin_attrs;
};3.6 __ATTR()
__ATTR() 是一个宏,定义如下:
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}该宏可以用于简化属性文件(attribute file)的定义。它用于定义一个 struct attribute 结构体,同时设置该属性的名称、权限以及关联的 show 和 store 操作函数。
- _name:属性的名称,将出现在 /sys 文件系统中的文件名,这个名称可以不带
" ",里面会自己处理成字符串。 - _mode:属性文件的权限模式,通常是八进制数,比如 0644、0666 等,表示文件的读写权限。
- _show:属性文件的读取函数指针,当用户读取该文件时将调用此函数。
- _store:属性文件的写入函数指针,当用户写入该文件时将调用此函数。
4. 属性文件的典型用例
一个典型的例子是 /sys/class/net/eth0/ 目录下的属性文件,它们允许用户查看或配置网络接口 eth0 的一些属性,比如 speed、mtu、operstate 等。

我们可以用以下命令读取相关信息:
cat /sys/class/net/eth0/speed # 读取速度
echo 1500 > /sys/class/net/eth0/mtu # 配置 MTU二、属性文件的创建与调用逻辑
1. sysfs_create_file()
接下来看一下属性文件怎么被创建的,我们从 sysfs_create_file() 函数入手,从这个函数入手,看一下是怎么个逻辑:

可以看到,我们创建 kobject 的时候,会调用到 sysfs_create_file() 函数来创建属性文件。
1.1 sysfs_create_file_ns()
sysfs_create_file() 函数内部调用了 sysfs_create_file_ns(),我们看一下 sysfs_create_file_ns() 函数:
int sysfs_create_file_ns(struct kobject *kobj, const struct attribute *attr,
const void *ns)
{
kuid_t uid;
kgid_t gid;
BUG_ON(!kobj || !kobj->sd || !attr);
kobject_get_ownership(kobj, &uid, &gid);
return sysfs_add_file_mode_ns(kobj->sd, attr, false, attr->mode,
uid, gid, ns);
}
EXPORT_SYMBOL_GPL(sysfs_create_file_ns);这里一共调用了两个函数,我们主要关注这个 sysfs_add_file_mode_ns() 函数。
1.2 sysfs_add_file_mode_ns()
sysfs_add_file_mode_ns() 函数定义如下:
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
const struct attribute *attr, bool is_bin,
umode_t mode, kuid_t uid, kgid_t gid, const void *ns)
{
struct lock_class_key *key = NULL;
const struct kernfs_ops *ops;
struct kernfs_node *kn;
loff_t size;
if (!is_bin) {
struct kobject *kobj = parent->priv;
const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;
/* every kobject with an attribute needs a ktype assigned */
if (WARN(!sysfs_ops, KERN_ERR
"missing sysfs attribute operations for kobject: %s\n",
kobject_name(kobj)))
return -EINVAL;
if (sysfs_ops->show && sysfs_ops->store) {
if (mode & SYSFS_PREALLOC)
ops = &sysfs_prealloc_kfops_rw;
else
ops = &sysfs_file_kfops_rw;
} else if (sysfs_ops->show) {
//......
} else if (sysfs_ops->store) {
// ......
} else
ops = &sysfs_file_kfops_empty;
size = PAGE_SIZE;
} else {
//......
}
//......
return 0;
}这里大概得逻辑就是从 kobj-> ktype 获取 sysfs_ops ,这个成员是 struct sysfs_ops 类型,它的定义如下:
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};从后面的一堆 if 中,可以知道,当我们定义了这两个函数的时候,ops 指针就会指向 sysfs_prealloc_kfops_rw 或者 sysfs_file_kfops_rw,具体指向哪一个,受到 mode 的影响,这个 mode 其实就是创建 kobject 的时候的属性的 mode 成员,表示读写权限,我们一般想要读写的话,这里会赋值为 0664,这是一个 8 进制数,SYSFS_PREALLOC 为 010000,这也是八进制,所以这里&运算是不成立的,所以最后指向的是 sysfs_file_kfops_rw,这个变量定义如下:
static const struct kernfs_ops sysfs_file_kfops_rw = {
.seq_show = sysfs_kf_seq_show,
.write = sysfs_kf_write,
};这里后面的逻辑我就没有去详细分析了,但是大概就是,当对属性文件进行读写的时候就会去调用 sysfs_file_kfops_rw 中的这两个函数,这里相当于是告诉读写的时候要用哪两个函数,就像驱动注册的时候我们会提供操作函数集一样。
1.3 总结
上面大概就是属性文件的创建过程,其实并没有分析的很完整,但是其实也没啥必要,大概知道个流程就行了,再分析就涉及很多东西了,这里就不花费时间去分析了。但是足够我们后面去找属性文件的读写逻辑了。
2. 属性文件的读写逻辑
前面呢,分析了一堆,大概可以知道,创建文件的时候,为这个文件的操作注册了对应的读写函数,就是 sysfs_file_kfops_rw 这个变量中的两个函数。但是最终是怎么操作的?我们以读的函数为例来看一下。
2.1 sysfs_kf_seq_show()
sysfs_kf_seq_show() 函数定义如下:
static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{
struct kernfs_open_file *of = sf->private;
struct kobject *kobj = of->kn->parent->priv;
const struct sysfs_ops *ops = sysfs_file_ops(of->kn);
ssize_t count;
char *buf;
//......
if (ops->show) {
count = ops->show(kobj, of->kn->priv, buf);
if (count < 0)
return count;
}
//......
return 0;
}这里可以看到定义了一个 struct sysfs_ops 类型指针,指向了 sysfs_file_ops() 的返回值,最后调用了其中的 show 函数,具体这个私有信息是怎么来的,应该文件系统那边去做的这里暂时就不详细去分析了。我们目标只有一个,就是找到这个 show 函数。
2.2 sysfs_file_ops()
sysfs_file_ops() 函数定义如下:
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{
struct kobject *kobj = kn->parent->priv;
if (kn->flags & KERNFS_LOCKDEP)
lockdep_assert_held(kn);
return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}到这里其实大概就比较清楚了,sysfs_file_ops() 函数返回的其实就是 ktype 中的 sysfs_ops 操作函数集。
2.3 总结
这里不再深入去分析了,内核太庞大了,以后有机会再说。到这里,我们大概知道了属性文件大概的调用逻辑。处理过程大概就是:
在 cat/echo 属性文件时(读/写属性文件写数据时)
- (1)先调用 sysfs_file_ops() 获取到
kobj->ktype->sysfs_ops 指针 - (2)调用对应内核的 show/store 函数。
从这里可以得出两条结论:
对于用户空间来讲,只负责把数据丢给内核的 store 以及从内核的 show 函数获取数据,至于 store 的数据用来做什么和 show 获取到数据表示什么意思则由内核决定。
如果从属的 kobject(就是 attribute 文件所在的目录)没有 ktype,或者没有 ktype-> sysfs_ops 指针,是不允许它注册任何 attribute 的。
参考资料:
【1】linux 设备驱动(9)attribute 详解 - Action_er - 博客园
【2】linux 驱动-设备驱动模型(属性文件 kobject )_linux 设备模型 kobject-CSDN 博客
【3】Linux 驱动开发—设备模型框架 kobject 创建属性文件_修改 kobject 创建的文件操作权限-CSDN 博客