kobject

1.Kobject结构

每个在内核中注册的kobject都对应于sysfs文件系统中的一个目录。

struct kobject {

    const char        *name;                    //kobject的名称

    struct list_head    entry;            //kobject 之间的双向链表,与所属的kset形成环形链表

    struct kobject        *parent;        //父kobject结构体

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

    struct kobj_type    *ktype;            //kobject的类型描述符,负责对该kobject类型进行跟踪的struct kobj_type的指针

    struct sysfs_dirent    *sd;            //sysfs文件目录,表示该内核对象在sys中对应的目录项的实例

    struct kref        kref;                        //kobject引用计数

    unsigned int state_initialized:1;    //kobject是否初始化

    unsigned int state_in_sysfs:1;//该内核对象在sysfs中已建立入口点

        unsigned int state_add_uevent_sent:1;当该对象发生变化时,不通过其所属的kset向用户空间发送uevent信号

        unsigned int state_remove_uevent_sent:1;

 struct dentry        * dentry;/*sysfs文件系统中与该对象对应的文件节点路径指针*/
    wait_queue_head_t    poll;/*等待队列头*/

};

假如有一个设备A。音频设备则应该有音量属性,则音量属性将在A设备目录下有个音量属性文件。在使用设备时,如果要改变音量大小,则可以写属性文件入音量指。得到音量大小时,可以读取属性文件中的音量值。

2.Kobject初始化

初始化一个kobject结构体变量,kobject_init函数(lib/kobject.c),调用此函数前应先将kobject变量成员全部置0

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

{

    kobject_init_internal(kobj);    //进一步初始化kobj内部成员

    kobj->ktype = ktype;    //将参数中传来的ktype变量赋值给kobj的ktype变量。

    return;

 error:

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

         dump_stack();//内核查看函数调用流程

}

分析kobject_init_internal函数(lib/kobject.c),此函数主要设置一些kobj中的一些变量

static void kobject_init_internal(struct kobject *kobj)

{

    if (!kobj)    //kobj是否为空

        return;

    kref_init(&kobj->kref);    //增加kobject的引用计数,kref_set(kref, 1);

    INIT_LIST_HEAD(&kobj->entry);    //初始化kobj的链表

    kobj->state_in_sysfs = 0;    //kobject还没有注册到sysfs中

    kobj->state_add_uevent_sent = 0;    //

    kobj->state_remove_uevent_sent = 0;

    kobj->state_initialized = 1;

}

3.kobj_type

1、 它是被嵌入kobject对象的类型,每个kobject对象应有相对应的ktype类型。

 2、它的作用:kobject创建和销毁时,应该做哪些操作。

struct kobj_type {

    void (*release)(struct kobject *kobj);    //释放函数(驱动编写时提供),此函数会被kobject_put函数调用

    struct sysfs_ops *sysfs_ops;    //属性文件的操作函数(只有读和写操作)

    struct attribute **default_attrs;    //属性数组

};

讨论kobj_type和kobject的关系,就要先说说kobject的引用。引用一个kobject使用函数kobject_get()这个函数会增加kobject的引用并返回kobject的指针。增加其引用是通过其kobject中断哦kref变量完成的。对kobject的引用管理主要是为了知道被引用的情况,如引用不为0就不能销毁kobject对象,引用为0时则调用相应的释放函数等。

struct kobject *kobject_get(struct kobject *kobj)

{

    if (kobj)

        kref_get(&kobj->kref);

    return kobj;

}

void kref_get(struct kref *kref)

{

    WARN_ON(!atomic_read(&kref->refcount));

    atomic_inc(&kref->refcount);    //将kref中的这个原子变量加1

    smp_mb__after_atomic_inc();

}

减少一个kobject对象的引用使用函数kobject_put()。当一个kobject对象的引用被减少到0时,程序就应该释放这个kobject相关的资源。所以在减少引用的函数中就应该有调用释放资源的相关代码,在下面内核代码中我们可以看到。

void kobject_put(struct kobject *kobj)

{

    if (kobj) {

        if (!kobj->state_initialized)    //若kobj没有初始化就不能减少其引用

            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); //减少kref计数

    }

}

int kref_put(struct kref *kref, void (*release)(struct kref *kref))

{

    WARN_ON(release == NULL);    //为空警告

    WARN_ON(release == (void (*)(struct kref *))kfree); //如果release函数就是kfree,则警告(即release函数不能是简单的kfree)

    if (atomic_dec_and_test(&kref->refcount)) {    //递减原子变量并检查其值

        release(kref);    //回调release函数

        return 1;

    }

    return 0;

}

那这个release函数在哪里保存呢,这就和kobj_type结构有关系了。上面我们可以看到kobj_type中有一个release函数指针,就是保存在这里。每一个kobject的ktype都指向一个kobj_type,它保存了这个kobject类型的release函数指针。

sysfs_ops

指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两 个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属 性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。

default_attrs

指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认 属性。属性给定了对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应 的作为文件而导出。其定义如下:

struct attribute {

    const char      *name;

    struct module       *owner;

    mode_t          mode;

};

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


release 函数和 kobject 类型
引用计数不由创建 kobject 的代码直接控制,当 kobject 的最后引用计数消失时,必须异步通知,而后kobject中ktype所指向的kobj_type结构体包含的release函数会被调用。通常原型如下: 
void my_object_release(struct kobject *kobj)
{
struct my_object *mine = container_of(kobj, struct my_object, kobj); 
/* Perform any additional cleanup on this object, then... */
kfree(mine);
}
每个 kobject 必须有一个release函数, 并且这个 kobject 必须在release函数被调用前保持不变( 稳定状态 ) 。这样,每一个 kobject 需要有一个关联的 kobj_type 结构,指向这个结构的指针能在 2 个不同的地方找到:
(1)kobject 结构自身包含一个成员(ktype)指向kobj_type ;
(2)如果这个 kobject 是一个 kset 的成员, kset 会提供kobj_type 指针。 

4.将kobject注册进sysfs系统

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

 {

         struct kobject *kobj;

         int retval;


        kobj = kobject_create();

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

         return kobj;

 }

看看kobject_add()实现流程:

kobject_add()->kobject_add_varg()->kobject_set_name_vargs()->kobject_add_internal()->create_dir()->sysfs_create_dir() ; 从这个流程可以看到注册一个kobject实际上就是在sysfs文件系统中创建一个目录.前面几个函数都是较简单的,这里主要看看kobject_add_internal()函数:

158 static int kobject_add_internal(struct kobject *kobj)

159 {

160         int error = 0;

161         struct kobject *parent;

172

173         parent = kobject_get(kobj->parent);

174

176         if (kobj->kset) {

177                 if (!parent)

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

179                 kobj_kset_join(kobj);

180                 kobj->parent = parent;

181         }

182         error = create_dir(kobj);

205         kobj->state_in_sysfs = 1;

206

207         return error;

208 }

判断kobj的名字,我们的目录是要有名字的. ,增加kobj父亲的引用计数

 如果kobj设置了kset, 在kobj未指定父对象的情况下,把ket->kobj设为这个kobj的父亲,并将kobj对象添加到kset的kobj list中.

create_dir(),就是在sysfs下创建一个目录文件撒.

我们在看一下如何将kobject注册进sysfs系统中。使用函数kobject_init_and_add()函数将一个kobject注册进sysfs系统,在/sys中表现为生成一个相应的目录。

增加 kobject 到 kset 中去,通常是在kobject 创建时完成,其过程分为3步:
(1)完成kobject的初始化,特别注意entry,parent的初始化。
(2)把kobject 的 kset 成员指向目标kset。
(3)将kobject 传递给下面的函数:

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);            //调用初始化函数先初始化kobject变量

    va_start(args, fmt);            //解析可变参数列表

    retval = kobject_add_varg(kobj, parent, fmt, args); //给kobject添加参数,并且将其添加到sysfs系统。

    va_end(args);                            //结束解析参数列表

    return retval;

}

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);        //设置kobject的名称

    kobj->parent = parent;            //设置kobject的父kobject

    return kobject_add_internal(kobj);    //添加kobject

}

static int kobject_add_internal(struct kobject *kobj)

{

    int error = 0;

    struct kobject *parent;

    parent = kobject_get(kobj->parent);    //获得父kobject,并增加父kobject的引用计数

    /* join kset if set, use it as parent if we do not already have one */

    if (kobj->kset) {                                        //是否有kset集合

        if (!parent)                                            //如果没有父kobject则用kset中的kobject对象

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

        kobj_kset_join(kobj);            //将kobject添加进它关联的kset的list链表中。

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

    }

    error = create_dir(kobj);                //创建kobject的相应目录

    kobj->state_in_sysfs = 1;        //标记为已经注册进sysfs

    return error;

}

sysfs创建目录函数create_dir,在lib/kobject.c

static int create_dir(struct kobject *kobj)

{

    int error = 0;

    if (kobject_name(kobj)) {

        error = sysfs_create_dir(kobj);            //在sysfs中创建目录,将来有时间了可以分析下sysfs子系统。

        if (!error) {

            error = populate_dir(kobj);

            if (error)

                sysfs_remove_dir(kobj);

        }

    }

    return error;

}

还有一个函数kobject_add,也可以添加一个kobject,它只是没有kobject_init这一步。

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;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值