目录
一、概述
将以前的笔记整理一下
二、kobject
2.1 说明
kobject(kernel object)实际上是顶层抽象基类,它的主要功能有:
- 引用计数(reference count),当其为0时释放
- 建立层次结构(hierarchy):通过parent指针,指向上层kobject
- 利用建立的结构,配合sysfs,将kobject信息导出到用户空间
2.2 数据抽象
struct kobject {
const char *name; /* 外部数据:属性 */
struct list_head entry; /* 内部数据:层次结构 */
struct kobject *parent; /* 外部数据:层次结构 */
struct kset *kset; /* 外部数据:层次结构 */
struct kobj_type *ktype; /* 外部数据:方法 */
struct kernfs_node *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
- 结构 entry, parent, kset,sd,kref
- 方法 ktype
结构分成两部分含义,一部分只在数据结构内部使用,外界不感知,如 parent,sd,kref。一部分要与外部其他模块共同组成结构,如entry,parent
看下ktype:
struct 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);
};
release和kobject释放有关系,由于object层层被继承,当子类的实例销毁时,重载release就好。
sysfs_ops和default_attrs和sysfs文件系统有关,用于显示kobj相关的信息,实现时逐级调用上层的show/store实现。
2.3 接口
- void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
- int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
- int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
- struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
对一个kobject来说,初始化时需要指定属性,方法,和parent,如果继承kobject,这三个元素可以(必须)重载
2.4 实现
kref 用来实现引用计数
sd用来和sysfs交互,这里不具体展开,认为每个sysfs的节点与一个kobject相关连就好了,这样再通过parent就建立起一个关于kobject的树形结构
kobject_init就是完成动态的初始化——指定方法和内部基本结构
kobject_add就是将kobj加入到sysfs,设置name,这里需要注意:
- 如果parent为空,就设置为kobject所属的kset对应的kobject,如果没有对应的kset与之关联,就置空,这意味着kobject的节点在sysfs根目录下。
parent = kobject_get(kobj->parent);
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
一般的都通过使用kobject_init_and_add完成上述两个函数的功能。
而kobject_create_and_add用于那些位于sysfs根目录下的kobject(后来发现不一定,parent不一定是NULL),这时候kobject由函数自行分配(kobject_create),这个 函数指定了sysfs根下的 通用方法kobj_type
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
三 、kset
3.1 说明
kset本身继承了kobject,是一个特殊的kobj,主要功能是
1. 提供kobj的集合
2. 提供对容器内所有成员进行管理的机制:uevnet
3.2 数据抽象
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
- 继承 kobj
- 方法 uevent_ops
- 结构 list,list_lock
kset要管理位于容器内的kobj,使用list
3.3 接口
- void kset_init(struct kset *k)
- int kset_register(struct kset *k)
- struct kset *kset_create_and_add(const char *name, const struct kset_uevent_ops *uevent_ops, struct kobject *parent_kobj)
3.4 实现
void kset_init(struct kset *k)
{
kobject_init_internal(&k->kobj);
INIT_LIST_HEAD(&k->list);
spin_lock_init(&k->list_lock);
}
这里kset_init似乎实现的不那么考究,在我看来,既然kset继承于kobject,在初始化时就应该使用kobject提供的外部接口,但是一些object所谓的外部变量如name,ktype该如何传递呢?kset在提供接口时使用了kobject中的internal接口——即不会涉及到外部变量的传递
int kset_register(struct kset *k)
{
int err;
kset_init(k);
err = kobject_add_internal(&k->kobj);
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
可以看到kset_register在实现上和kset_init思路一样。在使用kset_register接口时,要自行初始化父类(对kset来说,name,ktype,parent)和自身uevent_ops
- uevent_ops
- name, parent(kset), ktype
kset_create_and_add提供了动态分配的API版本,和kobject类似,该接口没有更上层的kset了:
/*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL;
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error;
kset = kset_create(name, uevent_ops, parent_kobj);
error = kset_register(kset);
return kset;
}
四、uevent
4.1 说明
前面说到kobject提供将内核信息导出到用户空间的功能,这是通过uvent机制实现的
4.2 数据抽象
enum kobject_action {
KOBJ_ADD,
KOBJ_REMOVE,
KOBJ_CHANGE,
KOBJ_MOVE,
KOBJ_ONLINE,
KOBJ_OFFLINE,
KOBJ_MAX
};
4.3 接口
将指定的kobject指定kobject_actio导出到用户态:
- int kobject_uevent(struct kobject *kobj, enum kobject_action action)
- int kobject_uevent_env(struct kobject *kobj, enum kobject_action action, char *envp_ext[])
4.4 实现
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
char *envp_ext[])
{
/* search the kset we belong to */
top_kobj = kobj;
while (!top_kobj->kset && top_kobj->parent)
top_kobj = top_kobj->parent;
if (!top_kobj->kset) {
pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "
"without kset!\n", kobject_name(kobj), kobj,
__func__);
return -EINVAL;
}
kset = top_kobj->kset;
uevent_ops = kset->uevent_ops;
if (kobj->uevent_suppress) {
pr_debug("kobject: '%s' (%p): %s: uevent_suppress "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
/* skip the event, if the filter returns zero. */
if (uevent_ops && uevent_ops->filter)
if (!uevent_ops->filter(kset, kobj)) {
pr_debug("kobject: '%s' (%p): %s: filter function "
"caused the event to drop!\n",
kobject_name(kobj), kobj, __func__);
return 0;
}
}
- 必须找到kobject对应的kset
- 检查一些条件,kobj->uevent_suppress设置则丢弃
- uevent_ops->filter,使用kset管理kobject,如果容器不允许其通过,则丢弃
/* environment buffer */
env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);
if (!env)
return -ENOMEM;
/* complete object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
retval = -ENOENT;
goto exit;
}
/* default keys */
retval = add_uevent_var(env, "ACTION=%s", action_string);
if (retval)
goto exit;
retval = add_uevent_var(env, "DEVPATH=%s", devpath);
if (retval)
goto exit;
retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
if (retval)
goto exit;
/* keys passed in from the caller */
if (envp_ext) {
for (i = 0; envp_ext[i]; i++) {
retval = add_uevent_var(env, "%s", envp_ext[i]);
if (retval)
goto exit;
}
}
/* let the kset specific function add its stuff */
if (uevent_ops && uevent_ops->uevent) {
retval = uevent_ops->uevent(kset, kobj, env);
if (retval) {
pr_debug("kobject: '%s' (%p): %s: uevent() returned "
"%d\n", kobject_name(kobj), kobj,
__func__, retval);
goto exit;
}
}
- 如果可以发送,则要将发送的消息整合起来,通过kobj_uevent_env{}
- 将action_string,devpath,subsystem,额外信息envp_ext,uevent_ops->uevent,uevent_seqnum都加入消息中。
分析一下构造消息的过程:
struct kobj_uevent_env {
char *argv[3];
char *envp[UEVENT_NUM_ENVP];
int envp_idx;
char buf[UEVENT_BUFFER_SIZE];
int buflen;
};
将上述消息放到buf中,每单一的消息由envp指向,消息的数量envp_idx,这个功能由add_uevent_var实现,很简单,就不再分析了。
/* send netlink message */
list_for_each_entry(ue_sk, &uevent_sock_list, list) {
struct sock *uevent_sock = ue_sk->sk;
struct sk_buff *skb;
size_t len;
if (!netlink_has_listeners(uevent_sock, 1))
continue;
/* allocate message with the maximum possible size */
len = strlen(action_string) + strlen(devpath) + 2;
skb = alloc_skb(len + env->buflen, GFP_KERNEL);
if (skb) {
char *scratch;
/* add header */
scratch = skb_put(skb, len);
sprintf(scratch, "%s@%s", action_string, devpath);
/* copy keys to our continuous event payload buffer */
for (i = 0; i < env->envp_idx; i++) {
len = strlen(env->envp[i]) + 1;
scratch = skb_put(skb, len);
strcpy(scratch, env->envp[i]);
}
NETLINK_CB(skb).dst_group = 1;
retval = netlink_broadcast_filtered(uevent_sock, skb,
0, 1, GFP_KERNEL,
kobj_bcast_filter,
kobj);
/* ENOBUFS should be handled in userspace */
if (retval == -ENOBUFS || retval == -ESRCH)
retval = 0;
} else
retval = -ENOMEM;
}
- 通过netlink构造一个skb将整合过的消息发出去。
五、总结
kobject和kset的关系结构上可以用下图表示:
蓝色箭头构建的是sysfs,黑色箭头方便kset管理容器内的kobject,红色箭头便于kobject找到所属kset