在 Linux 内核中,属性文件(Attribute Files)指的是通过 sysfs
文件系统暴露给用户空间的文件,这些文件通常用于在用户空间与内核空间之间进行简单的数据交换或配置。
sysfs
文件系统 是 Linux 内核中的一种虚拟文件系统,它将内核对象 (kobjects
) 的属性映射为文件,通常位于 /sys
目录下。这些文件通过 sysfs
文件系统暴露给用户,可以被用户空间的程序读取或写入。
kobject
是内核对象管理机制的核心,sysfs
文件系统通过 kobject
来组织和管理这些属性文件。
读取内核状态:属性文件可以暴露内核对象的状态或统计信息,例如设备的当前状态、计数器值等。
配置内核参数:用户可以通过写入属性文件来修改内核对象的参数,配置设备或调试内核模块。
读取操作 (show
):用户空间读取属性文件的内容,触发内核中的 show
函数,返回文件的内容。
写入操作 (store
):用户空间写入属性文件的内容,触发内核中的 store
函数,更新内核对象的状态或参数。
一个典型的例子是 /sys/class/net/eth0/
目录下的属性文件,它们允许用户查看或配置网络接口 eth0
的一些属性,比如 speed
, mtu
, operstate
等。
例如:
cat /sys/class/net/eth0/speed
echo 1500 > /sys/class/net/eth0/mtu
当您在内核模块中定义一个 kobject
时,通常会定义一组 attribute
结构体,并将它们关联到 sysfs
文件系统中。每个属性文件在 sysfs
中对应一个 attribute
结构体,通常会包含以下信息:
name
: 属性文件的名称。mode
: 文件的权限,例如 0444
表示只读,0666
表示可读可写。show
函数: 定义了如何显示属性文件的内容。store
函数: 定义了如何处理用户写入的内容。#include
#include
#include
#include
#include
#define DISABLE 0
#define ENABLE 1
// 创建自己的kobj ,附带两个属性
struct mykobj
{
struct kobject kobj;
int reg_count;
int reg_status;
};
struct mykobj *mykobject01;
static void dynamic_kobj_release(struct kobject *kobj)
{
struct mykobj *mykobject01 = container_of(kobj, struct mykobj, kobj);
kfree(mykobject01);
}
static struct attribute attr1 = {
.name = "reg_count",
.mode = 0666,
};
static struct attribute attr2 = {
.name = "reg_status",
.mode = 0666,
};
ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
if (strcmp(attr->name, "reg_count") == 0)
return sprintf(buf, "%d\n", mykobject01->reg_count);
if (strcmp(attr->name, "reg_status") == 0)
return sprintf(buf, "%d\n", mykobject01->reg_status);
return 0;
}
ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
int val;
if (strcmp(attr->name, "reg_count") == 0)
{
sscanf(buf, "%d", &val);
mykobject01->reg_count = val;
}
else if (strcmp(attr->name, "reg_status") == 0)
{
sscanf(buf, "%d", &val);
mykobject01->reg_status = val;
}
return count;
}
static const struct sysfs_ops my_sysfs_ops = {
.show = my_show,
.store = my_store,
};
static struct attribute *attrs[] = {&attr1, &attr2, NULL};
static struct kobj_type my_type = {
.release = dynamic_kobj_release,
.sysfs_ops = &my_sysfs_ops,
.default_attrs = attrs,
};
static int __init mykobj_init(void)
{
int ret = 0;
mykobject01 = kzalloc(sizeof(struct mykobj), GFP_KERNEL);
if (!mykobject01)
return -ENOMEM;
mykobject01->reg_count = 2;
mykobject01->reg_status = DISABLE;
ret = kobject_init_and_add(&mykobject01->kobj, &my_type, NULL, "%s", "mykobj_root");
if (ret)
{
kobject_put(&mykobject01->kobj);
return ret;
}
return 0;
}
static void __exit mykobj_exit(void)
{
kobject_put(&mykobject01->kobj);
}
module_init(mykobj_init);
module_exit(mykobj_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple example of making kobject with sysfs attributes");
属性文件是内核与用户空间进行交互的重要方式之一。它们通常用于在内核模块中暴露一些简单的、可配置的参数,便于用户空间的应用程序进行读取或配置。这种机制在驱动开发、调试以及设备配置中广泛使用。
上述暴露出一个问题,如果某个设备属性文件过多,如何优化if的次数?这样会让读写文件很臃肿,最好的方法,应该是一个属性对应一个show和store
可以使用ATTR宏定义来实现这个功能
__ATTR()
是 Linux 内核中的一个宏,用于简化属性文件(attribute file)的定义。它用于定义一个 struct attribute
结构体,同时设置该属性的名称、权限以及关联的 show
和 store
操作函数。
__ATTR()
宏的定义#define __ATTR(_name, _mode, _show, _store) { \
.attr = { .name = __stringify(_name), .mode = (_mode) }, \
.show = (_show), \
.store = (_store), \
}
__ATTR()
宏的参数_name
:属性的名称,将出现在 /sys
文件系统中的文件名。_mode
:属性文件的权限模式,通常是八进制数,比如 0644
、0666
等,表示文件的读写权限。_show
:属性文件的读取函数指针,当用户读取该文件时将调用此函数。_store
:属性文件的写入函数指针,当用户写入该文件时将调用此函数。#include
#include
#include
#include
#include
#include
#define DISABLE 0
#define ENABLE 1
// 创建自己的kobj ,附带两个属性
struct mykobj
{
struct kobject kobj;
int reg_count;
int reg_status;
};
struct mykobj *mykobject01;
static void dynamic_kobj_release(struct kobject *kobj)
{
struct mykobj *mykobject01 = container_of(kobj, struct mykobj, kobj);
kfree(mykobject01);
}
/*
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);
};
*/
//reg_count_show 属性
static ssize_t reg_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
pr_info("Reading reg_count: %d\n", mykobject01->reg_count);
return sprintf(buf, "%d\n", mykobject01->reg_count);
}
static ssize_t reg_count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
sscanf(buf, "%d", &mykobject01->reg_count);
return count;
}
struct kobj_attribute reg_count_attr= __ATTR(reg_count,0664,reg_count_show,reg_count_store);
//reg_status 属性
static ssize_t reg_status_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
pr_info("Reading reg_status: %d\n", mykobject01->reg_status);
return sprintf(buf, "%d\n", mykobject01->reg_status);
}
static ssize_t reg_status_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
sscanf(buf, "%d", &mykobject01->reg_status);
return count;
}
struct kobj_attribute reg_status_attr= __ATTR(reg_status,0664,reg_status_show,reg_status_store);
ssize_t my_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct kobj_attribute *kobj_attr = container_of(attr,struct kobj_attribute,attr);
return kobj_attr->show(kobj,kobj_attr,buf);
}
ssize_t my_store(struct kobject *kobj, struct attribute *attr, const char *buf, size_t count)
{
struct kobj_attribute *kobj_attr = container_of(attr,struct kobj_attribute,attr);
kobj_attr->store(kobj,kobj_attr,buf,count);
return 0;
}
static const struct sysfs_ops my_sysfs_ops = {
.show = my_show,
.store = my_store,
};
static struct attribute *attrs[] = {®_status_attr.attr, ®_count_attr.attr, NULL};
static struct kobj_type my_type = {
.release = dynamic_kobj_release,
.sysfs_ops = &my_sysfs_ops,
.default_attrs = attrs,
};
static int __init mykobj_init(void)
{
int ret = 0;
mykobject01 = kzalloc(sizeof(struct mykobj), GFP_KERNEL);
if (!mykobject01)
return -ENOMEM;
mykobject01->reg_count = 2;
mykobject01->reg_status = DISABLE;
ret = kobject_init_and_add(&mykobject01->kobj, &my_type, NULL, "%s", "mykobj_root");
if (ret)
{
kobject_put(&mykobject01->kobj);
return ret;
}
return 0;
}
static void __exit mykobj_exit(void)
{
kobject_put(&mykobject01->kobj);
}
module_init(mykobj_init);
module_exit(mykobj_exit);
MODULE_AUTHOR("Marxist");
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("A simple example of making kobject with sysfs attributes");
这段代码的目的是创建一个内核对象(kobject
),并通过 sysfs 暴露两个属性(reg_count
和 reg_status
)供用户空间访问。用户可以通过读取或写入 /sys
目录下的文件来查看和修改这些属性。
struct mykobj
{
struct kobject kobj;
int reg_count;
int reg_status;
};
struct mykobj *mykobject01;
mykobj
结构体包含了一个 kobject
对象以及两个整型成员变量 reg_count
和 reg_status
。kobject
是内核对象管理的基础,它提供了 sysfs 的接口。struct kobj_attribute reg_count_attr = __ATTR(reg_count, 0664, reg_count_show, reg_count_store);
struct kobj_attribute reg_status_attr = __ATTR(reg_status, 0664, reg_status_show, reg_status_store);
__ATTR
宏定义了两个 kobj_attribute
类型的属性:reg_count_attr
和 reg_status_attr
。该宏将属性名称、访问权限和读写方法关联起来。static ssize_t reg_count_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
pr_info("Reading reg_count: %d\n", mykobject01->reg_count);
return sprintf(buf, "%d\n", mykobject01->reg_count);
}
static ssize_t reg_count_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count) {
sscanf(buf, "%d", &mykobject01->reg_count);
return count;
}
reg_count_show
和 reg_count_store
是 reg_count
属性的读写方法。
reg_count_show
: 读取 reg_count
的值,并将其格式化输出到 buf
。reg_count_store
: 从 buf
读取用户输入的值并更新 reg_count
。static const struct sysfs_ops my_sysfs_ops = {
.show = my_show,
.store = my_store,
};
sysfs_ops
结构体定义了如何处理 sysfs 文件的读写操作。my_show
和 my_store
实现了通用的属性读写逻辑,通过 container_of
获取属性对应的 kobj_attribute
,然后调用具体的 show
或 store
方法。