kobject和kset是整个设备驱动模型的基石。
kobject
每个kobject对应sysfs中的一个目录,sysfs中的层次结构通过kobject之间的联系确定。
kobject的意义和存在形式
kobject最早是用来作为引用计数,用来跟踪被嵌入对象(参考kobject的使用方式)生命周期。
而随着kernel的不断升级,kobject承担的任务越来越多。
- 被嵌入对象的引用计数
- sysfs node
- 通过kobject将设备模型实现为一个多层次的体系结构
- 热插拔时间处理(有待商榷,应该和kset有关系吧)
kobject的三个相关文件,分别是1
2
3include/linux/kobject.h
lib/kobject.c
Documentation/kobject.txt
kobject定义于include/linux/kobject.h中1
2
3
4
5
6
7
8
9
10
11
12
13
14struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct sysfs_dirent *sd;
struct kref kref;
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;
};
- name: 内核对象的名字,对应的sysfs节点名字也是该name
- entry:可以参考kobject_add一节,entry将作为链表单元,将Kobject加入所属的kset链表中
- parent: 该kobject的上层对象,构建kobject之间的层次化关系
- kset: kobject所属的kset对象,kset可以理解为subsystem
- kobj_type: 该Kobject的sysfs文件系统相关的操作和属性,参考kobject type一节
- sd: 对应目录项的实例
- kref: 引用计数值,追踪被嵌入对象的声明周期,被初始化为1
- state bit field:
问题:
- 1 kset中提到的subsystem和/dev/char/xx/subsystem是同一个吗?该subsystem是如何建立的?
kobject的使用方式
kobject的常用方式就是嵌在某一对象的数据结构中(类似于list_head的用法),比如cdev1
2
3
4
5
6
7
8struct cdev {
struct kobject kobj;
struct module *owner;
const struct file_operations *ops;
struct list_head list;
dev_t dev;
unsigned int count;
};
对应这种用法,一定会有通过container_of实现的获取被嵌入对象的地址函数。
kobject 函数
本节中会列举一些常用函数
- kobject_set_name: 为kobject设置name
- kobject_init
- kobject_add
- kobject_init_and_add: 合并kobject_init和kobject_add
- kobject_create
- kobject_create_and_add
- kobj_del
kobject_init
1 | static void kobject_init_internal(struct kobject *kobj) |
坑,EXPORT_SYMBOL原理
kobject_init主要做了
- 初始化Kref和list_head entry
- 初始化kobject->state bit field
- 初始化ktype
kobject_add
1 | int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...) |
以上做了kobj->parent = parent,设置了两个kobject之间的层次关系,重要的部分在kobject_add_internal(kobj)中。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static int kobject_add_internal(struct kobject *kobj)
{
//这里为什么将parent kobject的kref + 1
parent = kobject_get(kobj->parent);
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
error = create_dir(kobj);
kobj->state_in_sysfs = 1;
}
上部分代码其实主要做了四件事
- 1 增加parent计数
- 2 为kobj->parent赋值
- 3 create sysfs dir for kobject
- 4 设置state_in_sfsfs为1表示sysfs中已经添加好
关于2, 如果有parent参数传入,那么kobject->parent = parent. 如果没有,那么kobject->parent只能依靠kset->kobject,如果kset存在,kset->kobj就是kobject的parent,并且将kobject加入kset的kobject链表中。
这里就解释了kobject->entry的作用,作为链表单位,将会加入kset的链表中
关于3,create_dir位于lib/kobject.c中,主要是1
2sysfs_create_dir
populate_dir
在sysfs_create_dir中,如果kobject有parent,则根据kobj->parent, kobj创建sysfs_dirent,并赋值给kobj->sd.
sysfs中具体如何新建sysfs_dirent,会在Linux设备驱动模型系列-5-sysfs篇.md中详细说明。
在populate_dir函数中,将根据kobject的ktype->attribute建立sysfs文件。
首先,ktype是这样的:1
2
3
4
5
6
7struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
坑,kobj_type需要解释
而populate_dir是这样的:
1
2
3
4
5
6
7
8
9
10
11 static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
...
if (t && t->default_attrs) {
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);
...
}
}
}
将根据attr在kobj->sd对应的sfsfs目录下建立属性文件。
坑,这里建立的是属性文件吗?
坑,详细的分析attribute和属性文件的关系。
kobject_create
主要部分是Kobject_init,而区别在于采用默认kobj_type,也就是dynamic_kobj_ktype.1
2
3
4static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
kobject_del
直接上代码1
2
3
4
5
6
7
8
9
10
11void kobject_del(struct kobject *kobj)
{
if (!kobj)
return;
sysfs_remove_dir(kobj);
kobj->state_in_sysfs = 0;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent = NULL;
}
坑,为什么没有kfree(kobject),为什么要kobject_put(parent)?
对应于kobject_add_internal,为什么将kobject_get(parent)
kobject type
1 | struct kobj_type { |
在通过kobject_init初始化kobject时,会传入kobj_type函数。
其中三个部分需要讨论
- release: 是每个kobject必须要定义的函数,当该Kobject的计数变为0时,将会运行kobj_type->release函数,往往用于清楚对象。
- sysfs_ops
- attribute
关于sysfs_ops和attribute,定义如下1
2
3
4
5
6
7
8
9struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *,char *);
ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
struct attribute {
const char *name;
mode_t mode;
}
究竟上述两个部分的作用是什么呢?
在之前的kobject_add中,关于create_dir的部分本来是留到sysfs篇中,但是其中一部分和kobj_type,所以在这里提前说明。1
2
3
4
5kobject_add
kobject_add_internal
create_dir
sysfs_create_dir
populate_dir
其中pupulate_dir中调用了sysfs_create_file函数,该函数是这部分的重点,来看代码1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
struct attribute *attr;
int error = 0;
int i;
if (t && t->default_attrs) {
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);
if (error)
break;
}
}
return error;
}
populate_dir中,会轮询kobj->kobj_type中的attr,并以此调用sysfs_create_file函数。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
36iint sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
{
BUG_ON(!kobj || !kobj->sd || !attr);
return sysfs_add_file(kobj->sd, attr, SYSFS_KOBJ_ATTR);
}
//create an attribute file for an object
int sysfs_add_file(struct sysfs_dirent *dir_sd, const struct attribute *attr,
int type)
{
return sysfs_add_file_mode(dir_sd, attr, type, attr->mode);
}
int sysfs_add_file_mode(struct sysfs_dirent *dir_sd,
const struct attribute *attr, int type, mode_t amode)
{
umode_t mode = (amode & S_IALLUGO) | S_IFREG;
struct sysfs_addrm_cxt acxt;
struct sysfs_dirent *sd;
int rc;
sd = sysfs_new_dirent(attr->name, mode, type);
if (!sd)
return -ENOMEM;
sd->s_attr.attr = (void *)attr;
sysfs_dirent_init_lockdep(sd);
sysfs_addrm_start(&acxt, dir_sd);
rc = sysfs_add_one(&acxt, sd);
sysfs_addrm_finish(&acxt);
if (rc)
sysfs_put(sd);
return rc;
}
由上可知,kobject_add将根据kobj_type中的attr在kobject对应的dir下建立属性文件。
在用户空间中,如果要使用该属性文件。首先需要通过open(“name”, mode)打开该文件。open最终调用到系统sysfs_open_file,该函数
- buffer->ops = ops
- file->private_data = buffer
在用户空间对该文件进行读写时,将根据file->private_data获取buffer,并根据buffer->ops中的show或者store,进行相应的操作。
既然后sysfs_create_file,必然有移除sysfs file的函数,也就是sysfs_remove_file.
kobject和sysfs的关系
经过上面的分析之后,kobject和sysfs的关系已经慢慢浮出水面。
kobject可以说是sysfs目录节点所对应的实例,该目录节点中的属性文件是根据kobj_type中的attr创建,属性文件的读写是根据kobj_type中的show和store函数进行操作。
kobject和kset的关系
Example Code
上述只是讲述原理,因为是Linux设备驱动模型的基础部分,所以一般场合也很难看到其应用。
应该有两个Example code, 一个是内核中的code,另外一部分是example code, 用来做更加直观的展示用。