您的位置:首页 > 移动开发 > Objective-C

设备驱动模型的基石kobject

2015-01-23 15:21 357 查看
之前我们分析了引用计数kref,总结了sysfs提供的API,并翻译了介绍kobject原理及用法的文档。应该说准备工作做得足够多,kobject的实现怎么都可以看懂了,甚至只需要总结下API就行了。可我还是决定把kobject的实现代码从头分析一遍。一是因为kobject的代码很重要,会在设备驱动模型代码中无数次被用到,如果不熟悉的话可以说是举步维艰。二是为了熟悉linux的编码风格,为以后分析更大规模的代码奠定基础。

kobject的头文件在include/linux/kobject.h,实现在lib/kobject.c。闲话少说,上代码。

[cpp] view
plaincopyprint?

struct 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;

};

在struct kobject中,name是名字,entry是用于kobject所属kset下的子kobject链表,parent指向kobject的父节点,kset指向kobject所属的kset,ktype定义了kobject所属的类型,sd指向kobject对应的sysfs目录,kref记录kobject的引用计数,之后是一系列标志。

[cpp] view
plaincopyprint?

struct kobj_type {

void (*release)(struct kobject *kobj);

struct sysfs_ops *sysfs_ops;

struct attribute **default_attrs;

};

struct kobj_type就是定义了kobject的公共类型,其中既有操作的函数,也有公共的属性。其中release()是在kobject释放时调用的,sysfs_ops中定义了读写属性文件时调用的函数。default_attrs中定义了这类kobject公共的属性。

[cpp] view
plaincopyprint?

struct kset {

struct list_head list;

spinlock_t list_lock;

struct kobject kobj;

struct kset_uevent_ops *uevent_ops;

};

struct kset可以看成在kobject上的扩展,它包含一个kobject的链表,可以方便地表示sysfs中目录与子目录的关系。其中,list是所属kobject的链表头,list_lock用于在访问链表时加锁,kobj是kset的内部kobject,要表现为sysfs中的目录就必须拥有kobject的功能,最后的kset_uevent_ops定义了对发往用户空间的uevent的处理。我对uevent不了解,会尽量忽略。

[cpp] view
plaincopyprint?

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);

};

struct kobj_attribute是kobject在attribute上做出的扩展,添加了两个专门读写kobject属性的函数。无论是kobject,还是kset(说到底是kset内部的kobject),都提供了使用kobj_attribute的快速创建方法。

结构差不多介绍完了,下面看看实现。我所知道的代码分析风格,喜欢自顶向下的方式,从一个函数开始,介绍出一个函数调用树。在代码量很大,涉及调用层次很深的时候,确实要采用这种打洞的方式来寻找突破口。但这种自顶向下的方式有两个问题:一是很容易迷失,二是代码分析的难度会逐渐增大而不是减小。在茫茫的代码中,你一头下去,周围都是你不认识的函数,一个函数里调用了三个陌生的函数,其中一个陌生的函数又调用了五个更陌生的函数...不久你就会产生很强的挫败感。这就像走在沙漠上,你不知道终点在哪,也许翻过一个沙丘就到了,也许还有无数个沙丘。而且在这种分析时,人是逐渐走向细节,容易被细节所困扰,忽略了整体的印象与代码的层次感。所以,我觉得在分析代码时,也可以采用自底向上的方式,从细小的、内部使用的函数,到比较宏观的、供外部调用的函数。而且按照这种顺序来看代码,基本就是文件从头读到尾的顺序,也比较符合写代码的流程。linux代码喜欢在文件开始处攒内部静态函数,攒到一定程度爆发,突然实现几个外部API,然后再攒,再实现。而且之前的内部静态函数会反复调用到。linux代码写得很有层次感,除了内外有别,还把意思相近的,或者功能刚好相反的,或者使用时顺序调用的函数放在一起,很便于阅读。闲话少说,等你看完kobject的实现自然就清楚了。

[cpp] view
plaincopyprint?

static 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;

}



static int create_dir(struct kobject *kobj)

{

int error = 0;

if (kobject_name(kobj)) {

error = sysfs_create_dir(kobj);

if (!error) {

error = populate_dir(kobj);

if (error)

sysfs_remove_dir(kobj);

}

}

return error;

}

create_dir()在sysfs中创建kobj对应的目录,populate_dir()创建kobj中默认属性对应的文件。create_dir()正是调用populate_dir()实现的。

[cpp] view
plaincopyprint?

static int get_kobj_path_length(struct kobject *kobj)

{

int length = 1;

struct kobject *parent = kobj;



/* walk up the ancestors until we hit the one pointing to the

* root.

* Add 1 to strlen for leading '/' of each level.

*/

do {

if (kobject_name(parent) == NULL)

return 0;

length += strlen(kobject_name(parent)) + 1;

parent = parent->parent;

} while (parent);

return length;

}



static void fill_kobj_path(struct kobject *kobj, char *path, int length)

{

struct kobject *parent;



--length;

for (parent = kobj; parent; parent = parent->parent) {

int cur = strlen(kobject_name(parent));

/* back up enough to print this name with '/' */

length -= cur;

strncpy(path + length, kobject_name(parent), cur);

*(path + --length) = '/';

}



pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj),

kobj, __func__, path);

}



/**

* kobject_get_path - generate and return the path associated with a given kobj and kset pair.

*

* @kobj: kobject in question, with which to build the path

* @gfp_mask: the allocation type used to allocate the path

*

* The result must be freed by the caller with kfree().

*/

char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)

{

char *path;

int len;



len = get_kobj_path_length(kobj);

if (len == 0)

return NULL;

path = kzalloc(len, gfp_mask);

if (!path)

return NULL;

fill_kobj_path(kobj, path, len);



return path;

}

前面两个是内部函数,get_kobj_path_length()获得kobj路径名的长度,fill_kobj_path()把kobj路径名填充到path缓冲区中。

kobject_get_path()靠两个函数获得kobj的路径名,从攒函数到爆发一气呵成。

[cpp] view
plaincopyprint?





[cpp] view
plaincopyprint?

static void kobj_kset_join(struct kobject *kobj)

{

if (!kobj->kset)

return;



kset_get(kobj->kset);

spin_lock(&kobj->kset->list_lock);

list_add_tail(&kobj->entry, &kobj->kset->list);

spin_unlock(&kobj->kset->list_lock);

}



/* remove the kobject from its kset's list */

static void kobj_kset_leave(struct kobject *kobj)

{

if (!kobj->kset)

return;



spin_lock(&kobj->kset->list_lock);

list_del_init(&kobj->entry);

spin_unlock(&kobj->kset->list_lock);

kset_put(kobj->kset);

}

kobj_kset_join()把kobj加入kobj->kset的链表中,kobj_kset_leave()把kobj从kobj->kset的链表中去除,两者功能相对。

[cpp] view
plaincopyprint?

static void kobject_init_internal(struct kobject *kobj)

{

if (!kobj)

return;

kref_init(&kobj->kref);

INIT_LIST_HEAD(&kobj->entry);

kobj->state_in_sysfs = 0;

kobj->state_add_uevent_sent = 0;

kobj->state_remove_uevent_sent = 0;

kobj->state_initialized = 1;

}





static int kobject_add_internal(struct kobject *kobj)

{

int error = 0;

struct kobject *parent;



if (!kobj)

return -ENOENT;



if (!kobj->name || !kobj->name[0]) {

WARN(1, "kobject: (%p): attempted to be registered with empty "

"name!\n", kobj);

return -EINVAL;

}



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;

}



pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",

kobject_name(kobj), kobj, __func__,

parent ? kobject_name(parent) : "<NULL>",

kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");



error = create_dir(kobj);

if (error) {

kobj_kset_leave(kobj);

kobject_put(parent);

kobj->parent = NULL;



/* be noisy on error issues */

if (error == -EEXIST)

printk(KERN_ERR "%s failed for %s with "

"-EEXIST, don't try to register things with "

"the same name in the same directory.\n",

__func__, kobject_name(kobj));

else

printk(KERN_ERR "%s failed for %s (%d)\n",

__func__, kobject_name(kobj), error);

dump_stack();

} else

kobj->state_in_sysfs = 1;



return error;

}

kobject_init_internal()初始化kobj。

kobject_add_internal()把kobj加入已有的结构。

这两个函数看似无关,实际很有关系。在kobject中有好几个结构变量,但重要的只有两个,一个是kset,一个是parent。这两个都是表示当前kobject在整个体系中的位置,决不能自行决定,需要外部参与设置。那把kobject创建的过程分为init和add两个阶段也就很好理解了。kobject_init_internal()把一些能自动初始化的结构变量初始化掉,等外界设置了parent和kset,再调用kobject_add_internal()把kobject安在适当的位置,并创建相应的sysfs目录及文件。

[cpp] view
plaincopyprint?

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

va_list vargs)

{

const char *old_name = kobj->name;

char *s;



if (kobj->name && !fmt)

return 0;



kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);

if (!kobj->name)

return -ENOMEM;



/* ewww... some of these buggers have '/' in the name ... */

while ((s = strchr(kobj->name, '/')))

s[0] = '!';



kfree(old_name);

return 0;

}



/**

* kobject_set_name - Set the name of a kobject

* @kobj: struct kobject to set the name of

* @fmt: format string used to build the name

*

* This sets the name of the kobject. If you have already added the

* kobject to the system, you must call kobject_rename() in order to

* change the name of the kobject.

*/

int kobject_set_name(struct kobject *kobj, const char *fmt, ...)

{

va_list vargs;

int retval;



va_start(vargs, fmt);

retval = kobject_set_name_vargs(kobj, fmt, vargs);

va_end(vargs);



return retval;

}

kobject_set_name()是设置kobj名称的,它又调用kobject_set_name_vargs()实现。但要注意,这个kobject_set_name()仅限于kobject添加到体系之前,因为它只是修改了名字,并未通知用户空间。

[cpp] view
plaincopyprint?

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)

{

char *err_str;



if (!kobj) {

err_str = "invalid kobject pointer!";

goto error;

}

if (!ktype) {

err_str = "must have a ktype to be initialized properly!\n";

goto error;

}

if (kobj->state_initialized) {

/* do not error out as sometimes we can recover */

printk(KERN_ERR "kobject (%p): tried to init an initialized "

"object, something is seriously wrong.\n", kobj);

dump_stack();

}



kobject_init_internal(kobj);

kobj->ktype = ktype;

return;



error:

printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);

dump_stack();

}

kobject_init()就是调用kobject_init_internal()自动初始化了一些结构变量,然后又设置了ktype。其实这个ktype主要是管理一些默认属性什么的,只要在kobject_add_internal()调用create_dir()之前设置就行,之所以会出现在kobject_init()中,完全是为了与后面的kobject_create()相对比。

[cpp] view
plaincopyprint?

static 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);

if (retval) {

printk(KERN_ERR "kobject: can not set name properly!\n");

return retval;

}

kobj->parent = parent;

return kobject_add_internal(kobj);

}



/**

* kobject_add - the main kobject add function

* @kobj: the kobject to add

* @parent: pointer to the parent of the kobject.

* @fmt: format to name the kobject with.

*

* The kobject name is set and added to the kobject hierarchy in this

* function.

*

* 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 associted 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;



if (!kobj)

return -EINVAL;



if (!kobj->state_initialized) {

printk(KERN_ERR "kobject '%s' (%p): tried to add an "

"uninitialized object, something is seriously wrong.\n",

kobject_name(kobj), kobj);

dump_stack();

return -EINVAL;

}

va_start(args, fmt);

retval = kobject_add_varg(kobj, parent, fmt, args);

va_end(args);



return retval;

}

kobject_add()把kobj添加到体系中。但它还有一个附加功能,设置kobj的名字。parent也是作为参数传进来的,至于为什么kset没有同样传进来,或许是历史遗留原因吧。

[cpp] view
plaincopyprint?

int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,

struct kobject *parent, const char *fmt, ...)

{

va_list args;

int retval;



kobject_init(kobj, ktype);



va_start(args, fmt);

retval = kobject_add_varg(kobj, parent, fmt, args);

va_end(args);



return retval;

}

kobject_init_and_add()虽然是kobject_init()和kobject_add()的合并,但并不常用,因为其中根本没留下设置kset的空挡,这无疑不太合适。

[cpp] view
plaincopyprint?

int kobject_rename(struct kobject *kobj, const char *new_name)

{

int error = 0;

const char *devpath = NULL;

const char *dup_name = NULL, *name;

char *devpath_string = NULL;

char *envp[2];



kobj = kobject_get(kobj);

if (!kobj)

return -EINVAL;

if (!kobj->parent)

return -EINVAL;



devpath = kobject_get_path(kobj, GFP_KERNEL);

if (!devpath) {

error = -ENOMEM;

goto out;

}

devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);

if (!devpath_string) {

error = -ENOMEM;

goto out;

}

sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);

envp[0] = devpath_string;

envp[1] = NULL;



name = dup_name = kstrdup(new_name, GFP_KERNEL);

if (!name) {

error = -ENOMEM;

goto out;

}



error = sysfs_rename_dir(kobj, new_name);

if (error)

goto out;



/* Install the new kobject name */

dup_name = kobj->name;

kobj->name = name;



/* This function is mostly/only used for network interface.

* Some hotplug package track interfaces by their name and

* therefore want to know when the name is changed by the user. */

kobject_uevent_env(kobj, KOBJ_MOVE, envp);



out:

kfree(dup_name);

kfree(devpath_string);

kfree(devpath);

kobject_put(kobj);



return error;

}

kobject_rename()就是在kobj已经添加到系统之后,要改名字时调用的函数。它除了完成kobject_set_name()的功能,还向用户空间通知这一消息。

[cpp] view
plaincopyprint?

int kobject_move(struct kobject *kobj, struct kobject *new_parent)

{

int error;

struct kobject *old_parent;

const char *devpath = NULL;

char *devpath_string = NULL;

char *envp[2];



kobj = kobject_get(kobj);

if (!kobj)

return -EINVAL;

new_parent = kobject_get(new_parent);

if (!new_parent) {

if (kobj->kset)

new_parent = kobject_get(&kobj->kset->kobj);

}

/* old object path */

devpath = kobject_get_path(kobj, GFP_KERNEL);

if (!devpath) {

error = -ENOMEM;

goto out;

}

devpath_string = kmalloc(strlen(devpath) + 15, GFP_KERNEL);

if (!devpath_string) {

error = -ENOMEM;

goto out;

}

sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);

envp[0] = devpath_string;

envp[1] = NULL;

error = sysfs_move_dir(kobj, new_parent);

if (error)

goto out;

old_parent = kobj->parent;

kobj->parent = new_parent;

new_parent = NULL;

kobject_put(old_parent);

kobject_uevent_env(kobj, KOBJ_MOVE, envp);

out:

kobject_put(new_parent);

kobject_put(kobj);

kfree(devpath_string);

kfree(devpath);

return error;

}

kobject_move()则是在kobj添加到系统后,想移动到新的parent kobject下所调用的函数。在通知用户空间上,与kobject_rename()调用的是同一操作。

[cpp] view
plaincopyprint?

void 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;

}

kobject_del()仅仅是把kobj从系统中退出,相对于kobject_add()操作。

[cpp] view
plaincopyprint?

/**

* kobject_get - increment refcount for object.

* @kobj: object.

*/

struct kobject *kobject_get(struct kobject *kobj)

{

if (kobj)

kref_get(&kobj->kref);

return kobj;

}



/*

* kobject_cleanup - free kobject resources.

* @kobj: object to cleanup

*/

static void kobject_cleanup(struct kobject *kobj)

{

struct kobj_type *t = get_ktype(kobj);

const char *name = kobj->name;



pr_debug("kobject: '%s' (%p): %s\n",

kobject_name(kobj), kobj, __func__);



if (t && !t->release)

pr_debug("kobject: '%s' (%p): does not have a release() "

"function, it is broken and must be fixed.\n",

kobject_name(kobj), kobj);



/* send "remove" if the caller did not do it but sent "add" */

if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {

pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",

kobject_name(kobj), kobj);

kobject_uevent(kobj, KOBJ_REMOVE);

}



/* remove from sysfs if the caller did not do it */

if (kobj->state_in_sysfs) {

pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",

kobject_name(kobj), kobj);

kobject_del(kobj);

}



if (t && t->release) {

pr_debug("kobject: '%s' (%p): calling ktype release\n",

kobject_name(kobj), kobj);

t->release(kobj);

}



/* free name if we allocated it */

if (name) {

pr_debug("kobject: '%s': free name\n", name);

kfree(name);

}

}



static void kobject_release(struct kref *kref)

{

kobject_cleanup(container_of(kref, struct kobject, kref));

}



/**

* kobject_put - decrement refcount for object.

* @kobj: object.

*

* Decrement the refcount, and if 0, call kobject_cleanup().

*/

void kobject_put(struct kobject *kobj)

{

if (kobj) {

if (!kobj->state_initialized)

WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "

"initialized, yet kobject_put() is being "

"called.\n", kobject_name(kobj), kobj);

kref_put(&kobj->kref, kobject_release);

}

}

kobject_get()和kobject_put()走的完全是引用计数的路线。kobject_put()会在引用计数降为零时撤销整个kobject的存在:向用户空间发生REMOVE消息,从sysfs中删除相应目录,调用kobj_type中定义的release函数,释放name所占的空间。

看看前面介绍的API。

[cpp] view
plaincopyprint?

int kobject_set_name(struct kobject *kobj, const char *name, ...)

__attribute__((format(printf, 2, 3)));

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,

va_list vargs);

void kobject_init(struct kobject *kobj, struct kobj_type *ktype);

int __must_check kobject_add(struct kobject *kobj,

struct kobject *parent,

const char *fmt, ...);

int __must_check kobject_init_and_add(struct kobject *kobj,

struct kobj_type *ktype,

struct kobject *parent,

const char *fmt, ...);

void kobject_del(struct kobject *kobj);



int __must_check kobject_rename(struct kobject *, const char *new_name);

int __must_check kobject_move(struct kobject *, struct kobject *);



struct kobject *kobject_get(struct kobject *kobj);

void kobject_put(struct kobject *kobj);



char *kobject_get_path(struct kobject *kobj, gfp_t flag);

基本上概扩了kobject从创建到删除,包括中间改名字,改位置,以及引用计数的变动。

当然,kobject创建仍比较麻烦,因为ktype需要自己写。下面就是kobject提供的一种快速创建方法。

[cpp] view
plaincopyprint?

static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,

char *buf)

{

struct kobj_attribute *kattr;

ssize_t ret = -EIO;



kattr = container_of(attr, struct kobj_attribute, attr);

if (kattr->show)

ret = kattr->show(kobj, kattr, buf);

return ret;

}



static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,

const char *buf, size_t count)

{

struct kobj_attribute *kattr;

ssize_t ret = -EIO;



kattr = container_of(attr, struct kobj_attribute, attr);

if (kattr->store)

ret = kattr->store(kobj, kattr, buf, count);

return ret;

}



struct sysfs_ops kobj_sysfs_ops = {

.show = kobj_attr_show,

.store = kobj_attr_store,

};



static void dynamic_kobj_release(struct kobject *kobj)

{

pr_debug("kobject: (%p): %s\n", kobj, __func__);

kfree(kobj);

}



static struct kobj_type dynamic_kobj_ktype = {

.release = dynamic_kobj_release,

.sysfs_ops = &kobj_sysfs_ops,

};

这个就是kobject自身提供的一种kobj_type,叫做dynamic_kobj_ktype。它没有提供默认的属性,但提供了release函数及访问属性的方法。

[cpp] view
plaincopyprint?

struct kobject *kobject_create(void)

{

struct kobject *kobj;



kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);

if (!kobj)

return NULL;



kobject_init(kobj, &dynamic_kobj_ktype);

return kobj;

}



struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)

{

struct kobject *kobj;

int retval;



kobj = kobject_create();

if (!kobj)

return NULL;



retval = kobject_add(kobj, parent, "%s", name);

if (retval) {

printk(KERN_WARNING "%s: kobject_add error: %d\n",

__func__, retval);

kobject_put(kobj);

kobj = NULL;

}

return kobj;

}

在kobject_create()及kobject_create_add()中,使用了这种dynamic_kobj_ktype。这是一种很好的偷懒方法。因为release()函数会释放kobj,所以这里的kobj必须是kobject_create()动态创建的。这里的kobject_create()和kobject_init()相对,kobject_create_and_add()和kobject_init_and_add()相对。值得一提的是,这里用kobject_create()和kobject_create_and_add()创建的kobject无法嵌入其它结构,是独立的存在,所以用到的地方很少。



[cpp] view
plaincopyprint?

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差不多。

[cpp] view
plaincopyprint?

int kset_register(struct kset *k)

{

int err;



if (!k)

return -EINVAL;



kset_init(k);

err = kobject_add_internal(&k->kobj);

if (err)

return err;

kobject_uevent(&k->kobj, KOBJ_ADD);

return 0;

}

kset_register()最大的特点是简单,它只负责把kset中的kobject连入系统,并发布KOBJ_ADD消息。所以在调用它之前,你要先设置好k->kobj.name、k->kobj.parent、k->kobj.kset。

[cpp] view
plaincopyprint?

void kset_unregister(struct kset *k)

{

if (!k)

return;

kobject_put(&k->kobj);

}

kset_unregister()只是简单地释放创建时获得的引用计数。使用引用计数就是这么简单。

[cpp] view
plaincopyprint?

struct kobject *kset_find_obj(struct kset *kset, const char *name)

{

struct kobject *k;

struct kobject *ret = NULL;



spin_lock(&kset->list_lock);

list_for_each_entry(k, &kset->list, entry) {

if (kobject_name(k) && !strcmp(kobject_name(k), name)) {

ret = kobject_get(k);

break;

}

}

spin_unlock(&kset->list_lock);

return ret;

}

kset_find_obj()从kset的链表中找到名为name的kobject。这纯粹是一个对外的API。

[cpp] view
plaincopyprint?

static void kset_release(struct kobject *kobj)

{

struct kset *kset = container_of(kobj, struct kset, kobj);

pr_debug("kobject: '%s' (%p): %s\n",

kobject_name(kobj), kobj, __func__);

kfree(kset);

}



static struct kobj_type kset_ktype = {

.sysfs_ops = &kobj_sysfs_ops,

.release = kset_release,

};

与kobject相对的,kset也提供了一种kobj_type,叫做kset_ktype。

[cpp] view
plaincopyprint?

static struct kset *kset_create(const char *name,

struct kset_uevent_ops *uevent_ops,

struct kobject *parent_kobj)

{

struct kset *kset;

int retval;



kset = kzalloc(sizeof(*kset), GFP_KERNEL);

if (!kset)

return NULL;

retval = kobject_set_name(&kset->kobj, name);

if (retval) {

kfree(kset);

return NULL;

}

kset->uevent_ops = uevent_ops;

kset->kobj.parent = parent_kobj;



/*

* 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;



return kset;

}



/**

* kset_create_and_add - create a struct kset dynamically and add it to sysfs

*

* @name: the name for the kset

* @uevent_ops: a struct kset_uevent_ops for the kset

* @parent_kobj: the parent kobject of this kset, if any.

*

* This function creates a kset structure dynamically and registers it

* with sysfs. When you are finished with this structure, call

* kset_unregister() and the structure will be dynamically freed when it

* is no longer being used.

*

* If the kset was not able to be created, NULL will be returned.

*/

struct kset *kset_create_and_add(const char *name,

struct kset_uevent_ops *uevent_ops,

struct kobject *parent_kobj)

{

struct kset *kset;

int error;



kset = kset_create(name, uevent_ops, parent_kobj);

if (!kset)

return NULL;

error = kset_register(kset);

if (error) {

kfree(kset);

return NULL;

}

return kset;

}

kset_create()和kset_create_and_add()就是使用kset_type的快速创建函数。

说实话,使用kobject_create_and_add()的比较少见,但使用 kset_create_and_add()的情形还是见过一些的。比如sysfs中那些顶层的目录,就是单纯的目录,不需要嵌入什么很复杂的结构,用简单的kset_create_and_add()创建就好了。

[cpp] view
plaincopyprint?

static inline const char *kobject_name(const struct kobject *kobj)

{

return kobj->name;

}



static inline struct kset *to_kset(struct kobject *kobj)

{

return kobj ? container_of(kobj, struct kset, kobj) : NULL;

}



static inline struct kset *kset_get(struct kset *k)

{

return k ? to_kset(kobject_get(&k->kobj)) : NULL;

}



static inline void kset_put(struct kset *k)

{

kobject_put(&k->kobj);

}



static inline struct kobj_type *get_ktype(struct kobject *kobj)

{

return kobj->ktype;

}

这些是在kobject.h中的内联函数。这里内联函数更多的意思是方便,易于屏蔽内部实现。



以上就是kobject共800余行的代码实现,当然我们忽略了uevent的那部分。

事实证明,自底向上或者顺序的代码分析方法,还是很适合千行左右的代码分析。而且这样分析很全面,容易我们洞察整个模块的意图,从而在理解代码时从较高的抽象角度去看。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: