kobject系统分析

1 概述

要说kobject不得不说sysfs

sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核 数据结构及其属性,以及它们之间的关联到用户空间的方法。

sysfs 始终与 kobject 的底层结构紧密相关。

任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的共同祖先;例如:某些对象属于某个子系统。
kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义了面向文件 I/O 操作的方法,以提供对内核属性的读写。

    上面这段话是从内核文档Documentation/zh_CN/filesystems/sysfs.txt 中摘抄的, 主要说明了kobject的作用, 对于注册kobject其实就是在sysfs中创建目录。 在目录中添加属性则用于在sysfs中创建普通文件,读写这些属性文件将会调用内核中为该属性设置的读写回调函数. 所以属性文件用于内核空间和用户空间通信。了解linux系统的人都应该知道sysfs文件一般用于动态配置内核。这段文档没有说明属性组的概念,属性组就是一组属性,这组属性创建的文件在所属属性组目录下。(sysfs的根目录一般问/sys)。

所以kobject系统的拓扑结构大概如下图(注意图中kobject parent 也是kobject,只不过是下面kobject的父kobject):
在这里插入图片描述

    kobject 和 属性组(attribute_group)都对应目录,属性 (attribute)对应普通文件, kobject下既可以放属性组,也可以放属性, 这就是kobject描述的sysfs文件系统树状结构。

2 使用

    有了上述背景知识我们来看下如何添加一个kobject到sysfs,sysfs提供了两个比较常用的接口,用于创建sysfs文件

int sysfs_create_dir_ns(struct kobject *kobj, const char *new_name,
                        const void *new_ns)
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
int sysfs_create_group(struct kobject *kobj, struct attribute_group *grp)

    kobject使用sysfs_create_dir_ns 函数创建kobject目录, 使用sysfs_create_file函数创建属性文件,使用sysfs_create_group函数创建属性组目录以及该组下面的属性文件。

void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
int kobject_add(struct kobject *kobj, struct kobject *parent,
                const char *fmt, ...)
                
struct kobject *kobject_create(void)

struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)  
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
                         struct kobject *parent, const char *fmt, ...)
                                    
int kobject_rename(struct kobject *kobj, const char *new_name)

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

struct kobject *kobject_get(struct kobject *kobj)
void kobject_put(struct kobject *kobj)
void kobject_del(struct kobject *kobj)                                  

kobject导出的函数主要分为上面五组,每组用空行隔开。

  • kobject_init 和 kobject_add 是初初始化kobject和添加kobject到sysfs的步骤。
  • kobject_create 提供了一个创建和初始化kobject的封装,并提供了一些kobject创建和初始化的缺省行为。
  • kobject_create_and_add 封装了kobject_create 和 kobject_add函数,并提供了一些kobject创建和初始化以及添加kobject到sysfs的缺省行为。
  • kobject_init_and_add 封装了kobject_init 和 kobject_add 函数,并提供了一些kobject初始化以及添加kobject到sysfs的缺省行为。
  • kobject_rename 用于修改kobject目录名称,kobject_get_path用于获取kobject对应的文件路径,kobject_set_name设置kobject目录名称。
  • kobject_get 增加引用计数, kobject_put 减少引用计数,当计数为0的时候会调用kobject_del, 用户也可以调用kobject_del强行删除kobject。

    由此可见kobject系统即为我们提供了简单的创建函数,又为我们提供了高度可定制的接口。 最简单的添加kobject到sysfs的方法如下序列:

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

比较复杂的序列如下:

kobj =new a kobject
ktype = new a ktype
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)

或者:

kobj  = kobject_create(void)
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)

又或者:

kobj =new a kobject
ktype = new a ktype
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)

3 源码分析

    既然kobject_create_and_add 封装了整个kobject的创建到添加过程, 我们就以他为情景进行分析。

/**
 * kobject_create_and_add - create a struct kobject dynamically and register it with sysfs
 *
 * @name: the name for the kobject
 * @parent: the parent kobject of this kobject, if any.
 *
 * This function creates a kobject structure dynamically and registers it
 * with sysfs.  When you are finished with this structure, call
 * kobject_put() and the structure will be dynamically freed when
 * it is no longer being used.
 *
 * If the kobject was not able to be created, NULL will be returned.
 */
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
    struct kobject *kobj;
    int retval;

    kobj = kobject_create();
    if (!kobj)
        return NULL;

    retval = kobject_add(kobj, parent, "%s", name);
    if (retval) {
        printk(KERN_WARNING "%s: kobject_add error: %d\n",
               __func__, retval);
        kobject_put(kobj);
        kobj = NULL;
    }
    return kobj;
}

    该函数有两个参数name为kobject的名字,也是要创建到sysfs中文件夹的名字, parent则是父目录对应的kobject,只有确定了父目录才能在文件系统中确定路径。
     这个函数很简单,只是调用了kobject_create和kobject_add函数(kobject_create会调用kobject_init)。 在kobject_add函数添加失败的时候会通过kobject_put释放。

    kobject_create 为kobject创建和初始化的过程,代码如下:

 * kobject_create - create a struct kobject dynamically
 *
 * This function creates a kobject structure dynamically and sets it up
 * to be a "dynamic" kobject with a default release function set up.
 *
 * If the kobject was not able to be created, NULL will be returned.
 * The kobject structure returned from here must be cleaned up with a
 * call to kobject_put() and not kfree(), as kobject_init() has
 * already been called on this structure.
 */
struct kobject *kobject_create(void)
{
    struct kobject *kobj;

    kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
    if (!kobj)
        return NULL;

    kobject_init(kobj, &dynamic_kobj_ktype);
    return kobj;
}

    函数首先调用kzalloc 申请了kobject的内存,在调用kobject_init 初始化, 注意kobject_init的第二个参数为dynamic_kobj_ktype, 该参数用于设定kobject在sysfs操作中的一些回调函数,和kobject的一些配置。

/**
 * kobject_init - initialize a kobject structure
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 *
 * This function will properly initialize a kobject such that it can then
 * be passed to the kobject_add() call.
 *
 * After this function is called, the kobject MUST be cleaned up by a call
 * to kobject_put(), not by a call to kfree directly to ensure that all of
 * the memory is cleaned up properly.
 */
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
    char *err_str;

    if (!kobj) {
        err_str = "invalid kobject pointer!";
        goto error;
    }
    if (!ktype) {
        err_str = "must have a ktype to be initialized properly!\n";
        goto error;
    }
    if (kobj->state_initialized) {
        /* do not error out as sometimes we can recover */
        printk(KERN_ERR "kobject (%p): tried to init an initialized "
               "object, something is seriously wrong.\n", kobj);
        dump_stack();
    }

    kobject_init_internal(kobj);
    kobj->ktype = ktype;
    return;

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

    kobject的state_initialized字段用于记录kobject是否已经初始化,如果二次初始化打印警告日志, 函数只是对异常情况做了一些检查, 真正的初始化工作在kobject_init_internal中,另外 设置kobject->ktype字段。
     kobj_type定义如下:

struct kobj_type {
    void (*release)(struct kobject *kobj);
    const struct sysfs_ops *sysfs_ops;
    struct attribute **default_attrs;
    const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
    const void *(*namespace)(struct kobject *kobj);
};
  • release 为kobject释放的回调函数,用于清理工作(一般为kfree(kobject))。
  • sysfs_ops 则是读写kobject目录下属性文件时的回调。
  • default_attrs kobject 默认的属性,默认属性会在添加kobject的时候创建相应的属性文件,额外的属性则需要用户自行调用sysfs_create_file或者 sysfs_create_group添加。
  • child_ns_type 命令空间相关。
  • namespace 命令空间相关。

    下面我们来看下初始化的具体过程kobject_init_internal

static void kobject_init_internal(struct kobject *kobj)
{
        if (!kobj)
                return;
        kref_init(&kobj->kref);.
        INIT_LIST_HEAD(&kobj->entry);
        kobj->state_in_sysfs = 0;
        kobj->state_add_uevent_sent = 0;
        kobj->state_remove_uevent_sent = 0;
        kobj->state_initialized = 1;
}

    这个函数非常简单,就不说明了。在分析kobject_add 之前我们先来看下 kobject_create 中设置的默认kobj_type, 该值为dynamic_kobj_ktype。

static struct kobj_type dynamic_kobj_ktype = {
        .release        = dynamic_kobj_release,
        .sysfs_ops      = &kobj_sysfs_ops,
};
const struct sysfs_ops kobj_sysfs_ops = {
        .show   = kobj_attr_show,
        .store  = kobj_attr_store,
};

    缺省的kobject 设置的sysfs读写回调为kobj_sysfs_ops, 释放函数为dynamic_kobj_release, dynamic_kobj_release函数实现的比较简单,就是kfree释放kobject内存,这里就不列出了, kobj_sysfs_ops的实现我们后面再来说明。

    前面我们看了kobject创建和初始化的过程,要使kobject发挥作用还需要添加到sysfs中。

/**
 * kobject_add - the main kobject add function
 * @kobj: the kobject to add
 * @parent: pointer to the parent of the kobject.
 * @fmt: format to name the kobject with.
 *
 * The kobject name is set and added to the kobject hierarchy in this
 * function.
 *
 * If @parent is set, then the parent of the @kobj will be set to it.
 * If @parent is NULL, then the parent of the @kobj will be set to the
 * kobject associated with the kset assigned to this kobject.  If no kset
 * is assigned to the kobject, then the kobject will be located in the
 * root of the sysfs tree.
 *
 * If this function returns an error, kobject_put() must be called to
 * properly clean up the memory associated with the object.
 * Under no instance should the kobject that is passed to this function
 * be directly freed with a call to kfree(), that can leak memory.
 *
 * Note, no "add" uevent will be created with this call, the caller should set
 * up all of the necessary sysfs files for the object and then call
 * kobject_uevent() with the UEVENT_ADD parameter to ensure that
 * userspace is properly notified of this kobject's creation.
 */
int kobject_add(struct kobject *kobj, struct kobject *parent,
                const char *fmt, ...)
{
        va_list args;
        int retval;

        if (!kobj)
                return -EINVAL;

        if (!kobj->state_initialized) {
                printk(KERN_ERR "kobject '%s' (%p): tried to add an "
                       "uninitialized object, something is seriously wrong.\n",
                       kobject_name(kobj), kobj);
                dump_stack();
                return -EINVAL;
        }
        va_start(args, fmt);
        retval = kobject_add_varg(kobj, parent, fmt, args);
        va_end(args);

        return retval;
}

    函数首先保证调用执行要先初始化kobject,否则打印错误日志并返回。然后调用kobject_add_varg函数来添加kobject到sysfs系统。kobject_add_varg后面的可变参数用于拼装kobject的名称。

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);
        if (retval) {
                printk(KERN_ERR "kobject: can not set name properly!\n");
                return retval;
        }
        kobj->parent = parent;
        return kobject_add_internal(kobj);
}

    函数设置名称和父kobject,然后调用kobject_add_internal函数添加kobject到sysfs系统。

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的部分就不分析了,这个函数时机上最重要的就是调用create_dir(kobj) 来创建sysfs的目录。

static int create_dir(struct kobject *kobj)
{
        const struct kobj_ns_type_operations *ops;
        int error;

        error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
        if (error)
                return error;

        error = populate_dir(kobj);
        if (error) {
                sysfs_remove_dir(kobj);
                return error;
        }

        /*
         * @kobj->sd may be deleted by an ancestor going away.  Hold an
         * extra reference so that it stays until @kobj is gone.
         */
        sysfs_get(kobj->sd);

        /*
         * If @kobj has ns_ops, its children need to be filtered based on
         * their namespace tags.  Enable namespace support on @kobj->sd.
         */
        ops = kobj_child_ns_ops(kobj);
        if (ops) {
                BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
                BUG_ON(ops->type >= KOBJ_NS_TYPES);
                BUG_ON(!kobj_ns_type_registered(ops->type));

                sysfs_enable_ns(kobj->sd);
        }

        return 0;
}

    这部分涉及sysfs和ns,也不深入分析了。

    最后我们来看下 kobject属性的数据结构以及定义它的一个宏

struct kobj_attribute {
        struct attribute attr;
        ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
                        char *buf);
        ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
                         const char *buf, size_t count);
};

/**
 * Use these macros to make defining attributes easier. See include/linux/device.h
 * for examples..
 */
#define __ATTR(_name, _mode, _show, _store) {                           \
        .attr = {.name = __stringify(_name),                            \
                 .mode = VERIFY_OCTAL_PERMISSIONS(_mode) },             \
        .show   = _show,                                                \
        .store  = _store,                                               \
}

    可以使用__ATTR宏来定义kobj_attribute,类似如下

__ATTR(foo, 0664, foo_show, foo_store)

    这里foo为属性名称,0664为文件权限,foo_show为属性文件读回调,foo_store则为属性文件写回调。

    最后我们来看下sysfs如何回调属性文件的读写函数, 还记得dynamic_kobj_ktype中的kobj_sysfs_op,这是一个默认的实现

const struct sysfs_ops kobj_sysfs_ops = {
        .show   = kobj_attr_show,
        .store  = kobj_attr_store,
};
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
                              char *buf)
{
        struct kobj_attribute *kattr;
        ssize_t ret = -EIO;

        kattr = container_of(attr, struct kobj_attribute, attr);
        if (kattr->show)
                ret = kattr->show(kobj, kattr, buf);
        return ret;
}

static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
                               const char *buf, size_t count)
{
        struct kobj_attribute *kattr;
        ssize_t ret = -EIO;

        kattr = container_of(attr, struct kobj_attribute, attr);
        if (kattr->store)
                ret = kattr->store(kobj, kattr, buf, count);
        return ret;
}

    sysfs在读写属性的时候调用sysfs_ops的show或者store函数,这个函数有一个参数为struct attribute *attr,它实际为 kobj_attribute结构,实际上这个函数的实现只是调用kobj_attribute的show或者store函数。

    总的show和store的调用栈如下:
show

vfs_read->__vfs_read->kernfs_fop_read->seq_read->kernfs_seq_show->sysfs_kf_seq_show->kobj_attr_show

[18774.453811] Call Trace:
[18774.453823]  [<c16f8aa3>] dump_stack+0x41/0x52
[18774.453830]  [<c1062fbe>] warn_slowpath_common+0x8e/0xd0
[18774.453835]  [<f9bdd10d>] ? foo_show+0x1d/0x3c [kobject_example]
[18774.453838]  [<f9bdd10d>] ? foo_show+0x1d/0x3c [kobject_example]
[18774.453848]  [<c1339700>] ? current_is_single_threaded+0xb0/0xb0
[18774.453852]  [<c1063022>] warn_slowpath_null+0x22/0x30
[18774.453855]  [<f9bdd10d>] foo_show+0x1d/0x3c [kobject_example]
[18774.453863]  [<c11982bc>] ? __kmalloc+0xac/0x230
[18774.453866]  [<f9bdd0f0>] ? foo_store+0x40/0x40 [kobject_example]
[18774.453872]  [<c133970d>] kobj_attr_show+0xd/0x20
[18774.453880]  [<c1214a12>] sysfs_kf_seq_show+0xd2/0x170
[18774.453885]  [<c12132f4>] kernfs_seq_show+0x24/0x30
[18774.453891]  [<c11cd5f6>] seq_read+0xe6/0x360
[18774.453896]  [<c1213945>] kernfs_fop_read+0x45/0x60
[18774.453901]  [<c1213900>] ? kernfs_file_direct_read+0x110/0x110
[18774.453908]  [<c11ae4d6>] __vfs_read+0x26/0x80
[18774.453912]  [<c11ae5a7>] vfs_read+0x77/0x120

store

vfs_write->kernfs_fop_write->sysfs_kf_write->kobj_attr_store

[19066.385778] Call Trace:
[19066.385786]  [<c16f8aa3>] dump_stack+0x41/0x52
[19066.385791]  [<c1062fbe>] warn_slowpath_common+0x8e/0xd0
[19066.385794]  [<f9bdd0cd>] ? foo_store+0x1d/0x40 [kobject_example]
[19066.385796]  [<f9bdd0cd>] ? foo_store+0x1d/0x40 [kobject_example]
[19066.385798]  [<c1063022>] warn_slowpath_null+0x22/0x30
[19066.385801]  [<f9bdd0cd>] foo_store+0x1d/0x40 [kobject_example]
[19066.385805]  [<c11982bc>] ? __kmalloc+0xac/0x230
[19066.385808]  [<f9bdd0b0>] ? b_show+0x50/0x50 [kobject_example]
[19066.385814]  [<c133973b>] kobj_attr_store+0x1b/0x30
[19066.385819]  [<c121467d>] sysfs_kf_write+0x3d/0x50
[19066.385821]  [<c1214640>] ? sysfs_kf_bin_read+0xe0/0xe0
[19066.385824]  [<c12137ab>] kernfs_fop_write+0xfb/0x140
[19066.385827]  [<c12136b0>] ? kernfs_vma_page_mkwrite+0x80/0x80
[19066.385831]  [<c11ae386>] vfs_write+0xa6/0x1d0
[19066.385834]  [<c11ae765>] SyS_write+0x55/0xc0
[19066.385837]  [<c11c7fc5>] ? __close_fd+0x75/0xa0
[19066.385841]  [<c170395f>] sysenter_do_call+0x12/0x12
[19066.385843] ---[ end trace 9ea3de6ceabd4dc1 ]---

最后

    到这里kobject就分析的差不多了,不懂了还可以参考kobject内核文档,在内核目录Documentation/kobject.txt, 也可以参考内核提供的例子samples/kobject/kobject-example.c。

预告

    后面将分析kset的作用。

发布了113 篇原创文章 · 获赞 22 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/woai110120130/article/details/103881309