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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章