lib\kobject.c文件分析

来源:互联网 发布:正规的网络购彩平台 编辑:程序博客网 时间:2024/05/18 02:49
 

本文件的函数列表:

 

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文件之后:

 

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

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

首先要说明的是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;

}