读书人

lib\kobject.c资料分析

发布时间: 2012-11-22 00:16:41 作者: rapoo

lib\kobject.c文件分析

本文件的函数列表:

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

获取指定kobject的完整路径名

void kobject_init(struct kobject * kobj)

初始化kobj(引用为1,链表为空,设置kset宿主)

int kobject_add(struct kobject * kobj)

添加一个kobj

int kobject_register(struct kobject * kobj)

kobject注册函数

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

按格式化设置kobj->k_name(obj的名字)

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

kobject重命名

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

重新给kobj指定父kobj

void kobject_del(struct kobject * kobj)

从kset->list中删除kobj

void kobject_unregister(struct kobject * kobj)

卸载kobj,主要调用了kobject_del

void kobject_cleanup(struct kobject * kobj)

static void kobject_release(struct kref *kref)

释放kobj,核心操作是调用了kobj关联的ktype结构体中的release函数,然后将kset和父kobj引用记数减1

当kobject_put调用将指定的kobj的kref计数减到0的时候,就会自动调用kobject_release函数了。

static void dir_release(struct kobject *kobj)

默认的关联在ktype中的release函数,作用是释放了kobj占用的内存

struct kobject *kobject_kset_add_dir(struct kset *kset, struct kobject *parent, const char *name)

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

申请一个obj对象,关联上kset,parent等参数,然后调用kobject_register函数将刚申请的kobj注册,最后返回这个刚申请的kobj。这里通过对register函数的实际调用,更进一步的分析了register函数实现的过程细节

而kobject_addr_dir函数则是直接将参数透传给kobject_kset_add_dir,只是kset设置为NULL。

int kset_register(struct kset * k)

注册一个kset,实际上,这个函数的实现和kobject_register很相似,所以最后的区别,可能就需要在kobject_uevent中找了

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

写在在分析完kobject.c文件之后:

很幸运的,在网上有找到这一个图

lib\kobject.c资料分析

这一个图的理解,也贯穿着整份代码的分析。

首先要说明的是obj的层次,kset是kobj的集合,或者说宿主。其实在linux很多代码都是这样的分层结构,只是名字叫得不同而已,比如IIC的适配器就是client的宿主,或者说集合。然后kset之下的每一个kobj都挂载在kset->list链表之上,这又和I2C驱动中,每一个client都挂载在adp->clients链表之下如出一辙。所以kobj->kset理所当然的就指向了kset。

1、而一个不同之处在于,kset本身也包含了一个kobj,这个obj是kset->list下所有kobj的父设备。

2、而kobj的release操作则是放在另外一个结构体ktype中,在获取这个结构体时,是优先返回kset指向的ktype,如果kset没有指定ktype,才会返回kobj自己指的ktype。

3、kobj很大的作用是用name来创建文件夹,然后根据kobj->ktype->attr的属性来创建文件。

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

先看一下kobject.h的头文件中的定义:

一个枚举结构,表示动作

enum kobject_action {

KOBJ_ADD,

KOBJ_REMOVE,

KOBJ_CHANGE,

KOBJ_MOVE,

KOBJ_ONLINE,

KOBJ_OFFLINE,

KOBJ_MAX

};

//这个结构体很多资料都有说明,这里先直接COPY别人的说明

struct kobject {

const char * k_name; //指向设备名称的指针

struct kref kref; //对象引用计数,只有一个成员refcount

struct list_head entry; //挂接到kset->list中的单元(下图蓝线)

struct kobject * parent; //指向父对象的指针

struct kset * kset; //所属kset的指针

struct kobj_type * ktype; //指向其对象类型描述符的指针

struct sysfs_dirent * sd; //文件路径

};

Struct kobj_type {

void (*release)(struct kobject *); //释放kobject占用的资源

struct sysfs_ops * sysfs_ops; //指向sysfs操作表

struct attribute ** default_attrs; // sysfs文件系统缺省属性列表

};

attribute, 属性。它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件
名就是name。文件读写的方法对应于kobj_type中的sysfs_ops。

kset最重要的是建立上层(sub-system)和下层的(kobject)的关联性,换句话说,kset是kobject的集合。

struct kset {

struct kobj_type *ktype; //指向该kset对象类型描述符的指针

struct list_head list; //连接该kset中所有kobject的链表头(蓝箭头)

spinlock_t list_lock; //list上的锁

struct kobject kobj; //嵌入的kobject

struct kset_uevent_ops *uevent_ops; //指向热插拔操作表的指针

};

struct kset_uevent_ops {

int (*filter)(struct kset *kset, struct kobject *kobj);

const char *(*name)(struct kset *kset, struct kobject *kobj);

int (*uevent)(struct kset *kset, struct kobject *kobj,

struct kobj_uevent_env *env);

};

struct kobj_uevent_env {

char *envp[UEVENT_NUM_ENVP];

int envp_idx;

char buf[UEVENT_BUFFER_SIZE];

int buflen;

};

//子系统属性,用在最后一个函数中

struct subsys_attribute {

struct attribute attr; //属性

ssize_t (*show)(struct kset *, char *); //显示函数

ssize_t (*store)(struct kset *, const char *, size_t); //存储函数

};

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

这一组两个函数是创建kobj的文件夹以及属性文件

先看头文件里的一个函数,因为下面要用到

这个函数的作用是返回kobj_type指针,优先返回kobject->kset->ktype的,如果他没有就返回kobject->ktype的。之前有说,kset是kobject的集合,所以这个返回就意味着集合属性优先。

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

{

if (k->kset && k->kset->ktype)

return k->kset->ktype;

else

return k->ktype;

}

//根据kobj的ktype->attr属性,在kobj文件夹下创建每一个文件

static int populate_dir(struct kobject *kobj)

{

struct kobj_type *t = get_ktype(kobj); //ktype(kobject释放函数,ops,属性attr)

struct attribute * attr;

int error = 0;

int i;

//ktype存在 且 默认属性存在

if (t && t->default_attrs) {

//提取每一个属性创建一个文件,放在kobj文件夹下

for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {

if ((error = sysfs_create_file(kobj, attr)))

break;

}

}

return error;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//根据kobj->name和kobj->sd创建一个文件夹,然后调用populate_dir在该文件夹下创建具体的文件

static int create_dir(struct kobject *kobj)

{

int error = 0;

if (kobject_name(kobj)) { //获取kobj-> k_name

error = sysfs_create_dir(kobj); //创建一个目录

if (!error) {

if ((error = populate_dir(kobj))) //根据kobj的属性创建文件

sysfs_remove_dir(kobj); //如果失败则删除目录

}

}

return error;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//返回该链表头所在的kobject结构体

static inline struct kobject *to_kobj(struct list_head *entry)

{

return container_of(entry, struct kobject, entry);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//获取kobject文件名的整体长度(从根kobject开始累加)

static int get_kobj_path_length(struct kobject *kobj)

{

int length = 1;

struct kobject *parent = kobj;

do {

if (kobject_name(parent) == NULL) //本kobject没有名字,出错返回0

return 0;

length += strlen(kobject_name(parent)) + 1; //计算kobject名字的长度

parent = parent->parent; //获取父kobject

} while (parent);

return length;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//整理path为 “/xxx/xxxx…”结构

//由于kobj是只能从最底层向上找,所以这个函数的路径复制动作是从尾向前复制:

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

//他的这个特性很容易联想到,调用这个函数之前肯定需要先调用上面的get_kobj_path_length函数先计算好路径长度。

static void fill_kobj_path(

struct kobject *kobj, //最底层的kobject

char *path, //路径字符串

int length) //路径长度

{

struct kobject *parent;

--length;

//逆向循环至根节点

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

int cur = strlen(kobject_name(parent)); //获取name的长度

length -= cur;

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

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

}

pr_debug("%s: path = '%s'\n",__FUNCTION__,path);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//获取指定kobject的路径名(实际就是kobj->name连在一起)

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; //为0则出错

path = kzalloc(len, gfp_mask); //申请一块内存来保存路径

if (!path)

return NULL;

fill_kobj_path(kobj, path, len); //获取完整的路径

return path; //返回路径

}

EXPORT_SYMBOL_GPL(kobject_get_path);

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//初始化kobject对象(引用计数为1+1,entry链表为空,设置kobj的kset宿主)

void kobject_init(struct kobject * kobj)

{

if (!kobj)

return;

kref_init(&kobj->kref); //kref->refcount=1(只有一个成员引用kobj)

INIT_LIST_HEAD(&kobj->entry); //kset链表为空

kobj->kset = kset_get(kobj->kset); //kobj->kset指向kobj的宿主kset结构体,同时kobj->kref->refcount的引用计数加1(因为被宿主引用了),kset_get的具体实现方式见下面三个函数

}

//如果指针为空,则返回NULL,否则执行一个操作

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

{

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

}

//如果指针不为空,则调用kref_get使kobj的引用计数加1,然后把kobj原样返回

struct kobject *kobject_get(struct kobject * kobj)

{

if (kobj)

kref_get(&kobj->kref);

return kobj;

}

也就是说,to_kset(kobject_get(&k->kobj))等效于to_kset(&k->obj);

//返回kobj的宿主kset结构体

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

{

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

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//断开kobj->entry的链接

static void unlink(struct kobject * kobj)

{

//如果设置了宿主kset,则执行

if (kobj->kset) {

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

list_del_init(&kobj->entry); //断开链表的连接

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

}

kobject_put(kobj);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//添加一个kobject进他的宿主kset链表,主要操作有:

1、如果kobj->name没有指定,则给一个默认的NAME

2、设置kobj的父节点

3、如果kobj->kset有指定,则将kobj->entry加如到kset->list中

4、为kobj创建文件夹

int kobject_add(struct kobject *kobj)

{

int error = 0;

struct kobject *parent;

//引用加1,如果返回错误则说明kobj本身就为NULL

if (!(kobj = kobject_get(kobj)))

return -ENOENT;

//如果kobj->k_name指向地址为空,则设置name为“NO_NAME”

if (!kobj->k_name)

kobject_set_name(kobj, "NO_NAME");

//name为空,则错误

if (!*kobj->k_name) {

pr_debug("kobject attempted to be registered with no name!\n");

WARN_ON(1);

kobject_put(kobj); //释放引用计数

return -EINVAL;

}

parent = kobject_get(kobj->parent); //获取kobj的父节点

//如果kobj->kset不为空(宿主有指定)

//如果kobj->kset没有指定,那么这个kobject就成了野obj了

if (kobj->kset) {

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

//如果父节点为空,则父节点设置为宿主kset中的kobj

//如果这个地方迷糊了,那么就返回最开始的那个图看一眼

if (!parent)

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

//将entry挂载到kset->list,同样的,如果迷糊了,就看最开始的图的蓝箭头

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

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

kobj->parent = parent; //设置kobj的父节点

}

//这个函数在一开始就有分析:创建文件夹,并根据kobj的属性创建文件

error = create_dir(kobj);

if (error) { //创建失败

unlink(kobj); //断开kobj->entry的链接

kobject_put(parent); //父节点的应用计数减1

dump_stack(); //记录下函数的调用过程,便于以后查错用

}

return error;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//kobject的注册

//从这个函数可以看出,实际上kobject_add实际也是初始化设置的一个步骤

int kobject_register(struct kobject *kobj)

{

int error = -EINVAL;

if (kobj) { //如果kobj指针不为空

kobject_init(kobj); //初始化一个kobj

error = kobject_add(kobj); //将kobj添加进kobj->set中

//如果没有出错,就调用uevent函数,这个函数在另外一个文件中,很大,先放放

if (!error)

kobject_uevent(kobj, KOBJ_ADD);

}

return error;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//给指定的kobj设置name,用了格式化设置参数

int kobject_set_name(

struct kobject * kobj, //指定kobj

const char *fmt, //格式化字符串

...)

{

int error = 0;

int limit;

int need;

va_list args;

char *name;

//申请1K内存保存文件名,第一次申请内存是申请了一个最大长度来获取格式化字符传的长度

name = kmalloc(1024, GFP_KERNEL);

if (!name) { //申请失败

error = -ENOMEM;

goto done;

}

//从格式化字符串中取出格式化的数据

//vsnprintf函数的四个参数分别为:首地址,长度,带格式化的字符串,格式说明

//从这里可以看出,fmt本身就是函数传递进来的参数,说明了格式化的字符串。然后va_start的作用是将fmt的格式化说明部分关联到args中,从print函数的格式,我们可以推算出args的格式为“a,b,c…”等,所以va_arg函数就是按指定格式提取出”,”分隔出的变量

va_start(args, fmt);

need = vsnprintf(name, 1024, fmt, args); //获取字符串长度

va_end(args);

kfree(name); //释放内存

//根据长度重新申请内存

limit = need + 1;

name = kmalloc(limit, GFP_KERNEL);

if (!name) {

error = -ENOMEM;

goto done;

}

va_start(args, fmt);

need = vsnprintf(name, limit, fmt, args);

va_end(args);

//长度比指定的长度长,出错。实际上这种情况不太可能发生。

if (need >= limit) {

kfree(name);

error = -EFAULT;

goto done;

}

//释放kobj原来的name内存,并关联上新的内存

kfree(kobj->k_name);

kobj->k_name = name;

done:

return error;

}

EXPORT_SYMBOL(kobject_set_name);

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//kobject重命名

int kobject_rename(

struct kobject *kobj,

const char *new_name)

{

int error = 0;

const char *devpath = NULL;

char *devpath_string = NULL;

char *envp[2];

//kobj的引用计数加1

kobj = kobject_get(kobj);

if (!kobj)

return -EINVAL;

if (!kobj->parent)

return -EINVAL;

//宿主kset已经指定

if (kobj->kset) {

struct kobject *temp_kobj;

//虽然这个函数我还没有分析,不过从函数名和参数中,我们不难猜测,这个函数是根本kobj的名字,在kset链表中找寻是否有同名的kobject

//一个疑问,为什么在给kobject命名的时候没有做这个检测呢?

temp_kobj = kset_find_obj(kobj->kset, new_name);

if (temp_kobj) { //找到同名的kobject,减少引用计数,出错返回

printk(KERN_WARNING "kobject '%s' cannot be renamed "

"to '%s' as '%s' is already in existence.\n",

kobject_name(kobj), new_name, new_name);

kobject_put(temp_kobj);

return -EINVAL;

}

}

//获取kobj的路径

devpath = kobject_get_path(kobj, GFP_KERNEL);

if (!devpath) {

error = -ENOMEM;

goto out;

}

//申请一块内存来保存旧的路径(目前还是kobj的当前路径)

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_rename_dir(kobj, new_name);

if (!error) //成功,重新uevent

kobject_uevent_env(kobj, KOBJ_MOVE, envp);

out:

kfree(devpath_string); //释放内存

kfree(devpath);

kobject_put(kobj); //释放应用计数

return error;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

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); //obj引用

if (!kobj)

return -EINVAL;

new_parent = kobject_get(new_parent); //引用父obj

if (!new_parent) { //没有父obj

if (kobj->kset) //有属于的kset,则父obj为kset结构体中的kobj

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

}

//获取旧的路径,这部分与rename的处理相同

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;

//移动dir到新的obj目录中

error = sysfs_move_dir(kobj, new_parent);

if (error)

goto out;

//重新设置kobj->parent父obj

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;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//删除指定的kobj,核心操作就两步:

1、 删除目录

2、断开kobj的entry在kset->list中的链接

void kobject_del(struct kobject * kobj)

{

if (!kobj)

return;

sysfs_remove_dir(kobj);

unlink(kobj);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

void kobject_unregister(struct kobject * kobj)

{

if (!kobj) //空指针

return;

pr_debug("kobject %s: unregistering\n",kobject_name(kobj));

kobject_uevent(kobj, KOBJ_REMOVE);

kobject_del(kobj); //删除kobj

kobject_put(kobj); //减小本kobj的引用计数

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//这个函数主要是给后面的release用的

//这个函数的工作主要如下:

1、 调用obj关联的ktype中的release函数,并释放name占用的内存

2、 指定kobj的宿主kset和父kobj的引用计数减1

void kobject_cleanup(struct kobject *kobj)

{

struct kobj_type *t = get_ktype(kobj); //获取obj中的ktype成员

struct kset *s = kobj->kset; //获取obj的宿主kset

struct kobject *parent = kobj->parent; //获取obj的父kobj

const char *name = kobj->k_name; //获取obj的name

//type成员存在,且release指定,则调用release函数

//要注意一点的是,get_ktype中,如果kobj所属的kset->ktype存在的话,是返回kset->ktype的

if (t && t->release) {

t->release(kobj);

kfree(name); //释放name所占用的内存

}

if (s)

kset_put(s); //宿主的引用记数减1

kobject_put(parent); //父obj的引用记数减1

}

//这里传递进来的参数是引用记数,函数先是container获取该引用记数所在的kobj,然后调用上面的cleanup函数

static void kobject_release(struct kref *kref)

{

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

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//减少obj的引用计数,前面用到很多,只是一直没注意当kref减到0的时候,是会自动调用kobject_release函数的

void kobject_put(struct kobject * kobj)

{

if (kobj)

kref_put(&kobj->kref, kobject_release);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

static void dir_release(struct kobject *kobj)

{

kfree(kobj); //释放obj结构体占用的内存

}

static struct kobj_type dir_ktype = {

.release = dir_release, //关联了一个release函数

.sysfs_ops = NULL,

.default_attrs = NULL,

};

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//新建一个kobj对象,设置好kset,parent,name参数,挂上默认的ktype,然后将其注册进kset

struct kobject *kobject_kset_add_dir(

struct kset *kset,

struct kobject *parent, //不可为NULL

const char *name)

{

struct kobject *k;

int ret;

if (!parent)

return NULL;

//申请一块obj内存

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

if (!k)

return NULL;

//指定obj的宿主,父obj,ktype,名字

k->kset = kset;

k->parent = parent;

k->ktype = &dir_ktype;

kobject_set_name(k, name);

//将obj注册

ret = kobject_register(k);

if (ret < 0) { //注册失败则删除本obj

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

__func__, ret);

kobject_del(k);

return NULL;

}

return k;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//添加一个obj目录,实际就是调用了上面的kobject_kset_add_dir函数

struct kobject *kobject_add_dir(

struct kobject *parent,

const char *name)

{

return kobject_kset_add_dir(NULL, parent, name);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

在kobject_kset_add_dir中有一个对kobject_register函数调用的实例,那么我们就根据这个实际的应用在分析一次obj的注册函数:

调用之前的设置如下:(为了和后面的参数对应上,所以这里把k改成了kobj)

kobj->kset = kset;

kobj->parent = parent;

kobj->ktype = &dir_ktype;

kobject_set_name(kobj, name);

然后注册:

kobject_init(kobj);

kobject_add(kobj);

kobject_uevent(kobj, KOBJ_ADD);

最后一个函数因为我们还没有分析env,所以暂时忽略。

继续看进去——init:

kobj->kset = kset_get(kobj->kset);

à to_kset(kobject_get(&kobj->kset ->kobj)) //这里将kset中的obj引用了一次

à to_kset(&kobj->kset ->kobj)

à container_of(container_of(&kobj->kset ->kobj),struct kset,kobj)

//这回看得清楚一点了,kobj->kset->kobj,也就是kobj宿主kset中包含的那个kobj(迷糊了就回到最开始的那个图看看,kset包含的那个kobj),然后通过container也就获取到了kset指针,所以这个地方绕了一个大圈,实际的作用就是(kobject_get(&kobj->kset ->kobj)这里,kset的kobj引用加1。而to_kset实际没起什么作用,说白了就是1=1。

kobject_init(kobj)函数的功能如下:

kref_init(&kobj->kref); //初始引用计数为1

INIT_LIST_HEAD(&kobj->entry); //obj没有挂在kset->list上

kobj->kset = kset_get(kobj->kset); //kobj->kset->kobj的引用加1

然后就是add(以下代码去掉了很多错误检测的代码):

kobject_get(kobj); //kobj的引用加1

if (!kobj->k_name) kobject_set_name(kobj, "NO_NAME"); //给一个默认的名字

parent = kobject_get(kobj->parent);

if (kobj->kset) { //如果宿主存在

if (!parent) parent = kobject_get(&kobj->kset->kobj);

list_add_tail(&kobj->entry,&kobj->kset->list); //将kobj添加进kset->list中

kobj->parent = parent; //设置父obj

}

create_dir(kobj); //建立目录

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//初始化kset

void kset_init(struct kset * k)

{

kobject_init(&k->kobj); //初始化kset中的kobj

INIT_LIST_HEAD(&k->list); //将kset的list清空

spin_lock_init(&k->list_lock);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//kset的add,实际也就是将kset包含的那个kobj添加进去

int kset_add(struct kset * k)

{

return kobject_add(&k->kobj);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//注册一个kset

int kset_register(struct kset * k)

{

int err;

if (!k) //空指针,出错

return -EINVAL;

kset_init(k); //先将kset初始化(函数中也初始化了kset->kobj)

err = kset_add(k); //添加kset->obj

if (err)

return err;

kobject_uevent(&k->kobj, KOBJ_ADD);

return 0;

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//卸载函数

void kset_unregister(struct kset * k)

{

if (!k)

return;

kobject_unregister(&k->kobj);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//在kset链表中找寻名字为name的obj

struct kobject *kset_find_obj(

struct kset * kset,

const char * name)

{

//这个函数的实现很简单,从kset->list链表中取出每个kobj,然后strcmp比较是否和指定的name相等,相等就返回该obj

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

//subsystem子系统注册,实际就是直接调用kset_reg

int subsystem_register(struct kset *s)

{

return kset_register(s);

}

void subsystem_unregister(struct kset *s)

{

kset_unregister(s);

}

@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@

int subsys_create_file(

struct kset *s,

struct subsys_attribute *a)

{

int error = 0;

if (!s || !a) //有空指针,错误

return -EINVAL;

if (kset_get(s)) { //kset有包含的obj

error = sysfs_create_file(&s->kobj, &a->attr); //根据属性创建文件

kset_put(s); //释放引用

}

return error;

}


读书人网 >移动开发

热点推荐