1 概念

kobj 是内核抽象出来的通用对象模型,用于表示内核中的实体.

结构体: https://elixir.bootlin.com/linux/v6.17.1/source/include/linux/kobject.h#L64.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
// kobj
struct kobject {
const char *name; // kobj 对象的名称, 跟 sys/<父 name>/<name> 一致
struct list_head entry; // 与父 kobj 建立链接的链表, 是父 kobj 的子链表
struct kobject *parent; // 指向父 kobj
struct kset *kset; // 指向包含该 kobj 的 set 集合
const struct kobj_type *ktype; // 描述 kobj 的属性
struct kernfs_node *sd; /* sysfs directory entry */
struct kref kref; // 对该 kobj 的引用

unsigned int state_initialized:1;
unsigned int state_in_sysfs:1;
unsigned int state_add_uevent_sent:1;
unsigned int state_remove_uevent_sent:1;
unsigned int uevent_suppress:1;

#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
};


// ktype
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops; // 对 sysfs 的操作
const struct attribute_group **default_groups;
const struct kobj_ns_type_operations *(*child_ns_type)(const struct kobject *kobj);
const void *(*namespace)(const struct kobject *kobj);
void (*get_ownership)(const struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};

// kset
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj; // kset 的 kobj 表示
const struct kset_uevent_ops *uevent_ops;
} __randomize_layout;

// 可选
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);
};

// sysfs
// 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
};

关系图.

alt text

2 Demo

2.1 最小的 kobject demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

MODULE_LICENSE("GPL");

static struct kobject *demo_kobj;

/* 加载模块时创建 kobject */
static int __init kobj_demo_init(void)
{
demo_kobj = kobject_create_and_add("kobj_demo", kernel_kobj); // 这里如果为 NULL 则出现在 /sys/ 下
if (!demo_kobj)
return -ENOMEM;

pr_info("kobj_demo created at /sys/kernel/kobj_demo\n");
return 0;
}

/* 卸载时销毁 */
static void __exit kobj_demo_exit(void)
{
kobject_put(demo_kobj); // 非常重要,每次 put 都会 kref --
pr_info("kobj_demo removed\n");
}

module_init(kobj_demo_init);
module_exit(kobj_demo_exit);

这是一个空的 kobj,成功 insmod 之后会出现.

1
ls /sys/kernel/kobj_demo

2.2 带属性文件的 demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

MODULE_LICENSE("GPL");

static struct kobject *demo_kobj;
static int demo_value = 0;

/* show */
static ssize_t value_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", demo_value);
}

/* store */
static ssize_t value_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%d", &demo_value);
return count;
}

/* 这里用最基础的 attribute */
static struct attribute value_attr = {
.name = "value",
.mode = 0664,
};

static const struct sysfs_ops demo_sysfs_ops = {
.show = value_show,
.store = value_store,
};

static struct kobj_type demo_ktype = {
.sysfs_ops = &demo_sysfs_ops,
};

/* 初始化/退出 */
static int __init kobj_demo_init(void)
{
int ret;
demo_kobj = kobject_create_and_add("kobj_demo", kernel_kobj);
if (!demo_kobj)
return -ENOMEM;

/* 绑定 kobj_type,否则 sysfs_ops 无法工作 */
demo_kobj->ktype = &demo_ktype;

/* 注册属性 */
ret = sysfs_create_file(demo_kobj, &value_attr);
if (ret)
kobject_put(demo_kobj);

pr_info("kobj_demo with attribute(created via struct attribute) created\n");
return ret;
}

static void __exit kobj_demo_exit(void)
{
kobject_put(demo_kobj);
pr_info("kobj_demo removed\n");
}

module_init(kobj_demo_init);
module_exit(kobj_demo_exit);

同样 insmod 之后我们会看到.

1
2
3
4
5
6
7
8
# 查看
cat /sys/kernel/kobj_demo/value
0

# 修改
echo 42 > /sys/kernel/kobj_demo/value
cat /sys/kernel/kobj_demo/value
42

2.3 多属性的 kobj

我们也可以用 kobj_arrtibute 是对 attribute 的一层封装.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>

MODULE_LICENSE("GPL");

static int foo, bar;

/* show/store */
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", foo);
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%d", &foo);
return count;
}

// #define __ATTR(_name, _mode, _show, _store) { \
// .attr = {.name = __stringify(_name), \
// .mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
// .show = _show, \
// .store = _store, \
// }
static struct kobj_attribute foo_attr = __ATTR(foo, 0664, foo_show, foo_store);


static ssize_t bar_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", bar);
}
static ssize_t bar_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count)
{
sscanf(buf, "%d", &bar);
return count;
}
static struct kobj_attribute bar_attr = __ATTR(bar, 0664, bar_show, bar_store);

static struct attribute *demo_attrs[] = {
&foo_attr.attr,
&bar_attr.attr,
NULL,
};

static const struct attribute_group demo_attr_group = {
.attrs = demo_attrs,
};

static struct kobject *demo_kobj;

static int __init kobj_demo_init(void)
{
int ret;
demo_kobj = kobject_create_and_add("kobj_demo", kernel_kobj);
if (!demo_kobj)
return -ENOMEM;

ret = sysfs_create_group(demo_kobj, &demo_attr_group);
if (ret)
kobject_put(demo_kobj);

pr_info("kobj_demo with foo and bar created\n");
return ret;
}

static void __exit kobj_demo_exit(void)
{
kobject_put(demo_kobj);
}

module_init(kobj_demo_init);
module_exit(kobj_demo_exit);

同理.

1
2
cat /sys/kernel/kobj_demo/foo
echo 99 > /sys/kernel/kobj_demo/bar