kset说明

1 概述

    前面我们分析了kobject系统,知道kobjec对应于sysfs中的一个目录, 属性对应于sysfs中的普通目录, 属性可以用属性组来进行分组,方便管理。那么kobject如何分组呢,这就引出了kset,对kset就是一组kobject。

    先来看下内核文档怎么说的:

A kset is a group of kobjects. These kobjects can be of the same ktype
or belong to different ktypes. The kset is the basic container type for
collections of kobjects. Ksets contain their own kobjects, but you can
safely ignore that implementation detail as the kset core code handles
this kobject automatically.

    一个kset是一组kobject。这些kobjects可以使用相同的ktype,也可以是不同的ktype。kset是容纳多个kobject的基本容器类型。kset有自己的一个kobject,但你可以安全地忽略它,kset的代码会自动管理这个内部的kobject。

    看了这个描述好像有点一头雾水,我们来看下kset数据结构的定义以及kobject和kset的关系就大概可以理解了。

struct kset {
        struct list_head list;
        spinlock_t list_lock;
        struct kobject kobj;
        const struct kset_uevent_ops *uevent_ops;
};
struct kobject {
        const char              *name;
        struct list_head        entry;
        struct kobject          *parent;
        struct kset             *kset;
        struct kobj_type        *ktype;
     ......
};

kset

  • list: 链表头,用于挂载kset容器下的kobject(通过kobject的entry字段)。
  • kobject: 为kset内嵌的一个kobject, 可以理解成kset派生自kobject,系统使用这个kobject为kset创建目录。
  • uevent_ops: 一组uevent相关的操作。

看到这里就可以解释内核文档里面的话

kset有自己的一个kobject,但你可以安全地忽略它,kset的代码会自动管理这个内部的kobject。

    也就是说系统使用kset->kobject来创建目录,但是这个对象是kset自己维护的,使用者不用关心。我们后面会看到sysfs创建目录的过程全部使用这个kobject完成。

kobject
parent: 为kobject的父对象,也就是kobject的父目录。
kset: 所属的kset容器。

    那么一个kobject到底是创建在kset对应的目录下还是创建在parent对应的目录下呢?
    在kobject添加的时候有这样一段话,在kobject_add_internal函数中:

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

    也就是说优先以parent为准。只有不指定parent的时候才使用kset作为kobject的父目录。

2 使用

    有了上述背景知识我们来看下如何使用kset。 kset系统提供了如下api:

static struct kset *kset_create(const char *name,
                                const struct kset_uevent_ops *uevent_ops,
                                struct kobject *parent_kobj)
void kset_init(struct kset *k)
int kset_register(struct kset *k)
struct kset *kset_create_and_add(const char *name,
                                 const struct kset_uevent_ops *uevent_ops,
                                 struct kobject *parent_kobj)
                                 

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

static void kobj_kset_join(struct kobject *kobj)
static void kobj_kset_leave(struct kobject *kobj)

void kset_unregister(struct kset *k)

    kset api可以分为四组,每组用空格隔开(实际上有几个函数是static修饰的,并不算是api,但是比较重要):

  • 第一大组为kset创建和注册函数,主要用于创建数据接口和添加到sysfs系统,包含如下函数。
         kset_create 为一个static函数,用于创建kset数据结构。
         kset_init 用于初始化kset数据结构。
         kset_register 不但调用了kset_init对kset初始化,还调用kobject_add_internal将kset添加到sysfs中。
         kset_create_and_add 是一键创建kset和添加到sysfs的函数,它调用了kset_create 和 kset_register函数。
  • kset_find_obj 用于根据名称在kset中查找kobject。
  • kobj_kset_join 是将一个kobject添加到kset中,它是一个静态函数,所以是kobject系统内部做了这件事情, 用户只需要设置kobject的kset成员变量, 系统就会调用该函数。 kobj_kset_leave与kobj_kset_join的功能相反。
  • kset_unregister 注销kset。
  • kset_unregister 用于注销kset。

所以你可以这么创建和初始化一个kset:

kset_create_and_add()

又或者

kset_create()
kset_register()

3 源码分析

    既然kset_create_and_add 封装了整个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,
                                 const 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;
}

    参数name为kset的名字,也就是在sysfs下创建目录的名字, parent_kobj则用于指定父目录。uevent_ops这个参数是udev的操作回调函数。 函数调用了kset创建kset数据结构, kset_register将kset添加到系统中。添加失败则释放内存,返回NULL。我们进一步分析kset_create。

/**
 * kset_create - create a struct kset dynamically
 *
 * @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.  This structure can
 * then be registered with the system and show up in sysfs with a call to
 * kset_register().  When you are finished with this structure, if
 * kset_register() has been called, 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.
 */
static struct kset *kset_create(const char *name,
                                const 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, "%s", 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的kobject变量, ktype参数和parent_kobject参数都用于将kset注册到sysfs系统中。

/**
 * kset_register - initialize and add a kset.
 * @k: kset.
 */
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 用于初始化kobject以及添加kobject到sysfs系统中。 这里面主要的操作其实是调用kobject的初始化函数,来初始化kset->kobject操作, 主要调用的函数为kobject_add_internal函数,我们在kobject系统 中已经分析过这个函数,但当时跳过了kset中的部分。接下来我们会在分析下这个函数,主要关注kset的部分。

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)
                        WARN(1, "%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
                        WARN(1, "%s failed for %s (error: %d parent: %s)\n",
                             __func__, kobject_name(kobj), error,
                             parent ? kobject_name(parent) : "'none'");
        } else
                kobj->state_in_sysfs = 1;

        return error;
}

    这部分代码我们已经在概述中说过关于kset的部分, 主要是判断kobject的parent是否为空,如果为空则该kobject的kset->kobject将作为它的父kobject,也就是这个kobject目录将会创建到它属于的kset目录下面。所以用户要将一个kobject添加到kset下面,只需要设置kobject->kset字段就可以了,其他的kobject_add_internal都帮你做了。 kobj_kset_join函数设置了kobject和kset的数据结构之间的关系。

/* add the kobject to its kset's list */
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);
}

    kobj_kset_join函数将kobject挂到kobject的链表上, 这个过程需要使用自旋锁保护。

最后

    到这里kset我们就分析完了,它不过是一个kobject的扩展, 其实主要的作用都在uevent中体现。不过由于篇幅问题,这里就不分析了。不懂了还可以参考kobject内核文档,在内核目录Documentation/kobject.txt, 也可以参考内核提供的例子samples/kobject/kset-example.c。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值