Overview
Linux Driver模型的基础设施就是kobject,我们经常会发现kobject会被内嵌于其它的structure中使用。通过将kobject嵌入其它structure中,可以利用kobject的t特性来达成期望的目的。和kobject相关的结构还有kset和ktype。
Kobject
Kobject有name, reference count和一个特定的type。kobject也有parent pointer,这就使kobject可以组织成一定的层次架构。通常,kobject在sysfs的文件系统中也代表一个目录。一般kobject本身并没有特别的含义,相反它们通常被内嵌于其它的structure中,这样structure就可以利用kboject的特性。一个strucuture中最多只能定义一个kboject。
struct kobject {
const char *name; // name
struct list_head entry;
struct kobject *parent; //pointer to parent
struct kset *kset; //pointer to kset
struct kobj_type *ktype;
struct kref kref;
...
}
Ktype
每一个kobject都会有相应的ktype,它是指struct kobject中的ktype成员。ktype控制着如何create/destroy一个kobject。
struct kobj_type {
void (*release)(struct kobject *kobj);
const struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
...
}
Kset
Kset代表一组kobjects,这些kobjects可以有相同或不同的ktype. Kset也有自己的kobject。当kobject的parent为NULL的话,那么认为它的parent为kset的kobject,这种关系会体现在sysfs的目录结构中。
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
Kobject API
这里介绍下常见API内部做了哪些动作,为了方便表达,没有把所有的函数语句都贴出来,只列关键语句。
kobject_init()
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
kobject_init_internal(kobj);
kobj->ktype = ktype;
return;
}
static void kobject_init_internal(struct kobject *kobj) {
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
...
kobj->state_initialized = 1;
}
static inline void kref_init(struct kref *kref){
atomic_set(&kref->refcount, 1);
}
可以看到kobject_init()主要是对kobject的成员做初始化,同时refcount置成1.但同时也要注意,kobject_init()其实并没有对name,parent和kset这些变量赋值。
kobject_create()
动态创建一个kobject。
struct kobject *kobject_create(void){
struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
static void dynamic_kobj_release(struct kobject *kobj){
pr_debug("kobject: (%p): %s\n", kobj, __func__);
kfree(kobj);
}
这里可以看到它通过kzalloc()分配memory,会先把kobject的各项成员都赋值成0,然后kobject_init()会再赋值部分成员。
kernel/documentation/kobject.txt有这样提到,建议kobject尽量使用动态分配的。
Because kobjects are dynamic, they must not be declared statically or onthe stack, but instead, always allocated dynamically. Future versions ofthe kernel will contain a run-time check for kobjects that are createdstatically and will warn the developer of this improper usage.
Kobject_add()
贴上函数注释,描述最为精确
If @parent is set, then the parent of the @kobj will be set to it.If @parent is NULL, then the parent of the @kobj will be set to the kobject associated with the kset assigned to this kobject. If no kset is assigned to the kobject, then the kobject will be located in the root of the sysfs tree.
* * If this function returns an error, kobject_put() must be called to properly clean up the memory associated with the object. Under no instance should the kobject that is passed to this function be directly freed with a call to kfree(), that can leak memory.
* * Note, no "add" uevent will be created with this call, the caller should set up all of the necessary sysfs files for the object and then call kobject_uevent() with the UEVENT_ADD parameter to ensure that userspace is properly notified of this kobject's creation.
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...){
va_list args; int retval;
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args);
return retval;
}
int kobject_add_varg(struct kobject *kobj, struct kobject *parent, const char *fmt, va_list vargs){
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs); //set the name of kobject
kobj->parent = parent; //set kobject parent
return kobject_add_internal(kobj);
}
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs){
const char *s;
s = kvasprintf_const(GFP_KERNEL, fmt, vargs); //this operation may have malloc operation
kfree_const(kobj->name);
kobj->name = s;
return 0;
}
static int kobject_add_internal(struct kobject *kobj){
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;
}
error = create_dir(kobj); //create dir in the sysfs the parent dir is parent kobject
kobj->state_in_sysfs = 1;
return error;
}
Kobject_get()
Increase reference count for kobject
struct kobject *kobject_get(struct kobject *kobj){
kref_get(&kobj->kref); } //only increase the reference count
return kobj;
}
static inline void kref_get(struct kref *kref){ WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);
}
Kobject_put()
Decrement the refcount, and if 0, call kobject_cleanup().
void kobject_put(struct kobject *kobj){
if (kobj) {
kref_put(&kobj->kref, kobject_release);
}
}
static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)){
return kref_sub(kref, 1, release);
}
static inline int kref_sub(struct kref *kref, unsigned int count, void (*release)(struct kref *kref)){
if (atomic_sub_and_test((int) count, &kref->refcount)) {
release(kref);
return 1;
}
}
static void kobject_release(struct kref *kref){
struct kobject *kobj = container_of(kref, struct kobject, kref);
kobject_cleanup(kobj);
}
//free kobject resources
static void kobject_cleanup(struct kobject *kobj){
struct kobj_type *t = get_ktype(kobj);
const char *name = kobj->name;
/* remove from sysfs if the caller did not do it */
if (kobj->state_in_sysfs) {
kobject_del(kobj);
}
if (t && t->release) {
t->release(kobj);
}
/* free name if we allocated it */
if (name) {
kfree_const(name);
}
}
kset_register()
Initialize and add a kset.
int kset_register(struct kset *k){
kset_init(k);
err = kobject_add_internal(&k->kobj); //将kobject挂入parent,同时create dir in sysfs
kobject_uevent(&k->kobj, KOBJ_ADD);
return 0;
}
void kset_init(struct kset *k){
kobject_init_internal(&k->kobj); //初始化kobject成员变量
INIT_LIST_HEAD(&k->list);
spin_lock_init(&k->list_lock);
}
通过上述API的一些理解,可以了解到linux kernel是如何运作kobject的。