您的位置:首页 > 运维架构 > Linux

Linux内核部件分析 设备驱动模型的基石kobject

2016-06-19 10:50 513 查看
之前我们分析了引用计数kref,总结了sysfs提供的API,并翻译了介绍kobject原理及用法的文档。应该说准备工作做得足够多,kobject的实现怎么都可以看懂了,甚至只需要总结下API就行了。可我还是决定把kobject的实现代码从头分析一遍。一是因为kobject的代码很重要,会在设备驱动模型代码中无数次被用到,如果不熟悉的话可以说是举步维艰。二是为了熟悉linux的编码风格,为以后分析更大规模的代码奠定基础。

    kobject的头文件在include/linux/kobject.h,实现在lib/kobject.c。闲话少说,上代码。

struct kobject {  
    const char      *name;  
    struct list_head    entry;  
    struct kobject      *parent;  
    struct kset     *kset;  
    struct kobj_type    *ktype;  
    struct sysfs_dirent *sd;  
    struct kref     kref;  
    unsigned int state_initialized:1;  
    unsigned int state_in_sysfs:1;  
    unsigned int state_add_uevent_sent:1;  
    unsigned int state_remove_uevent_sent:1;  
    unsigned int uevent_suppress:1;  
};  

在struct kobject中,name是名字,entry是用于kobject所属kset下的子kobject链表,parent指向kobject的父节点,kset指向kobject所属的kset,ktype定义了kobject所属的类型,sd指向kobject对应的sysfs目录,kref记录kobject的引用计数,之后是一系列标志。

struct kobj_type {  
    void (*release)(struct kobject *kobj);  
    struct sysfs_ops *sysfs_ops;  
    struct attribute **default_attrs;  
};  

struct kobj_type就是定义了kobject的公共类型,其中既有操作的函数,也有公共的属性。其中release()是在kobject释放时调用的,sysfs_ops中定义了读写属性文件时调用的函数。default_attrs中定义了这类kobject公共的属性。

struct kset {  
    struct list_head list;  
    spinlock_t list_lock;  
    struct kobject kobj;  
    struct kset_uevent_ops *uevent_ops;  
};  

struct kset可以看成在kobject上的扩展,它包含一个kobject的链表,可以方便地表示sysfs中目录与子目录的关系。其中,list是所属kobject的链表头,list_lock用于在访问链表时加锁,kobj是kset的内部kobject,要表现为sysfs中的目录就必须拥有kobject的功能,最后的kset_uevent_ops定义了对发往用户空间的uevent的处理。我对uevent不了解,会尽量忽略。

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);  
};  

struct kobj_attribute是kobject在attribute上做出的扩展,添加了两个专门读写kobject属性的函数。无论是kobject,还是kset(说到底是kset内部的kobject),都提供了使用kobj_attribute的快速创建方法。

结构差不多介绍完了,下面看看实现。我所知道的代码分析风格,喜欢自顶向下的方式,从一个函数开始,介绍出一个函数调用树。在代码量很大,涉及调用层次很深的时候,确实要采用这种打洞的方式来寻找突破口。但这种自顶向下的方式有两个问题:一是很容易迷失,二是代码分析的难度会逐渐增大而不是减小。在茫茫的代码中,你一头下去,周围都是你不认识的函数,一个函数里调用了三个陌生的函数,其中一个陌生的函数又调用了五个更陌生的函数...不久你就会产生很强的挫败感。这就像走在沙漠上,你不知道终点在哪,也许翻过一个沙丘就到了,也许还有无数个沙丘。而且在这种分析时,人是逐渐走向细节,容易被细节所困扰,忽略了整体的印象与代码的层次感。所以,我觉得在分析代码时,也可以采用自底向上的方式,从细小的、内部使用的函数,到比较宏观的、供外部调用的函数。而且按照这种顺序来看代码,基本就是文件从头读到尾的顺序,也比较符合写代码的流程。linux代码喜欢在文件开始处攒内部静态函数,攒到一定程度爆发,突然实现几个外部API,然后再攒,再实现。而且之前的内部静态函数会反复调用到。linux代码写得很有层次感,除了内外有别,还把意思相近的,或者功能刚好相反的,或者使用时顺序调用的函数放在一起,很便于阅读。闲话少说,等你看完kobject的实现自然就清楚了。

static int populate_dir(struct kobject *kobj)  
{  
    struct kobj_type *t = get_ktype(kobj);  
    struct attribute *attr;  
    int error = 0;  
    int i;  
  
    if (t && t->default_attrs) {  
        for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {  
            error = sysfs_create_file(kobj, attr);  
            if (error)  
                break;  
        }  
    }  
    return error;  
}  
  
static int create_dir(struct kobject *kobj)  
{  
    int error = 0;  
    if (kobject_name(kobj)) {  
        error = sysfs_create_dir(kobj);  
        if (!error) {  
            error = populate_dir(kobj);  
            if (error)  
                sysfs_remove_dir(kobj);  
        }  
    }  
    return error;  
}  

create_dir()在sysfs中创建kobj对应的目录,populate_dir()创建kobj中默认属性对应的文件。create_dir()正是调用populate_dir()实现的。

static int get_kobj_path_length(struct kobject *kobj)  
{  
    int length = 1;  
    struct kobject *parent = kobj;  
  
    /* walk up the ancestors until we hit the one pointing to the 
     * root. 
     * Add 1 to strlen for leading '/' of each level. 
     */  
    do {  
        if (kobject_name(parent) == NULL)  
            return 0;  
        length += strlen(kobject_name(parent)) + 1;  
        parent = parent->parent;  
    } while (parent);  
    return length;  
}  
  
static void fill_kobj_path(struct kobject *kobj, char *path, int length)  
{  
    struct kobject *parent;  
  
    --length;  
    for (parent = kobj; parent; parent = parent->parent) {  
        int cur = strlen(kobject_name(parent));  
        /* back up enough to print this name with '/' */  
        length -= cur;  
        strncpy(path + length, kobject_name(parent), cur);  
        *(path + --length) = '/';  
    }  
  
    pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj),  
         kobj, __func__, path);  
}  
  
/** 
 * kobject_get_path - generate and return the path associated with a given kobj and kset pair. 
 * 
 * @kobj:   kobject in question, with which to build the path 
 * @gfp_mask:   the allocation type used to allocate the path 
 * 
 * The result must be freed by the caller with kfree(). 
 */  
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;  
    path = kzalloc(len, gfp_mask);  
    if (!path)  
        return NULL;  
    fill_kobj_path(kobj, path, len);  
  
    return path;  
}  

前面两个是内部函数,get_kobj_path_length()获得kobj路径名的长度,fill_kobj_path()把kobj路径名填充到path缓冲区中。

kobject_get_path()靠两个函数获得kobj的路径名,从攒函数到爆发一气呵成。

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);  
}  
  
/* remove the kobject from its kset's list */  
static void kobj_kset_leave(struct kobject *kobj)  
{  
    if (!kobj->kset)  
        return;  
  
    spin_lock(&kobj->kset->list_lock);  
    list_del_init(&kobj->entry);  
    spin_unlock(&kobj->kset->list_lock);  
    kset_put(kobj->kset);  
}  

kobj_kset_join()把kobj加入kobj->kset的链表中,kobj_kset_leave()把kobj从kobj->kset的链表中去除,两者功能相对。

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;  
}  
  
  
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)  
            printk(KERN_ERR "%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  
            printk(KERN_ERR "%s failed for %s (%d)\n",  
                   __func__, kobject_name(kobj), error);  
        dump_stack();  
    } else  
        kobj->state_in_sysfs = 1;  
  
    return error;  
}  

kobject_init_internal()初始化kobj。

kobject_add_internal()把kobj加入已有的结构。

这两个函数看似无关,实际很有关系。在kobject中有好几个结构变量,但重要的只有两个,一个是kset,一个是parent。这两个都是表示当前kobject在整个体系中的位置,决不能自行决定,需要外部参与设置。那把kobject创建的过程分为init和add两个阶段也就很好理解了。kobject_init_internal()把一些能自动初始化的结构变量初始化掉,等外界设置了parent和kset,再调用kobject_add_internal()把kobject安在适当的位置,并创建相应的sysfs目录及文件。

int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,  
                  va_list vargs)  
{  
    const char *old_name = kobj->name;  
    char *s;  
  
    if (kobj->name && !fmt)  
        return 0;  
  
    kobj->name = kvasprintf(GFP_KERNEL, fmt, vargs);  
    if (!kobj->name)  
        return -ENOMEM;  
  
    /* ewww... some of these buggers have '/' in the name ... */  
    while ((s = strchr(kobj->name, '/')))  
        s[0] = '!';  
  
    kfree(old_name);  
    return 0;  
}  
  
/** 
 * kobject_set_name - Set the name of a kobject 
 * @kobj: struct kobject to set the name of 
 * @fmt: format string used to build the name 
 * 
 * This sets the name of the kobject.  If you have already added the 
 * kobject to the system, you must call kobject_rename() in order to 
 * change the name of the kobject. 
 */  
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)  
{  
    va_list vargs;  
    int retval;  
  
    va_start(vargs, fmt);  
    retval = kobject_set_name_vargs(kobj, fmt, vargs);  
    va_end(vargs);  
  
    return retval;  
}  

kobject_set_name()是设置kobj名称的,它又调用kobject_set_name_vargs()实现。但要注意,这个kobject_set_name()仅限于kobject添加到体系之前,因为它只是修改了名字,并未通知用户空间。

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_init()就是调用kobject_init_internal()自动初始化了一些结构变量,然后又设置了ktype。其实这个ktype主要是管理一些默认属性什么的,只要在kobject_add_internal()调用create_dir()之前设置就行,之所以会出现在kobject_init()中,完全是为了与后面的kobject_create()相对比。

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_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 associted 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_add()把kobj添加到体系中。但它还有一个附加功能,设置kobj的名字。parent也是作为参数传进来的,至于为什么kset没有同样传进来,或许是历史遗留原因吧。

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);  
  
    va_start(args, fmt);  
    retval = kobject_add_varg(kobj, parent, fmt, args);  
    va_end(args);  
  
    return retval;  
}  

kobject_init_and_add()虽然是kobject_init()和kobject_add()的合并,但并不常用,因为其中根本没留下设置kset的空挡,这无疑不太合适。

int kobject_rename(struct kobject *kobj, const char *new_name)  
{  
    int error = 0;  
    const char *devpath = NULL;  
    const char *dup_name = NULL, *name;  
    char *devpath_string = NULL;  
    char *envp[2];  
  
    kobj = kobject_get(kobj);  
    if (!kobj)  
        return -EINVAL;  
    if (!kobj->parent)  
        return -EINVAL;  
  
    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;  
  
    name = dup_name = kstrdup(new_name, GFP_KERNEL);  
    if (!name) {  
        error = -ENOMEM;  
        goto out;  
    }  
  
    error = sysfs_rename_dir(kobj, new_name);  
    if (error)  
        goto out;  
  
    /* Install the new kobject name */  
    dup_name = kobj->name;  
    kobj->name = name;  
  
    /* This function is mostly/only used for network interface. 
     * Some hotplug package track interfaces by their name and 
     * therefore want to know when the name is changed by the user. */  
    kobject_uevent_env(kobj, KOBJ_MOVE, envp);  
  
out:  
    kfree(dup_name);  
    kfree(devpath_string);  
    kfree(devpath);  
    kobject_put(kobj);  
  
    return error;  
}  

kobject_rename()就是在kobj已经添加到系统之后,要改名字时调用的函数。它除了完成kobject_set_name()的功能,还向用户空间通知这一消息。

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);  
    if (!kobj)  
        return -EINVAL;  
    new_parent = kobject_get(new_parent);  
    if (!new_parent) {  
        if (kobj->kset)  
            new_parent = kobject_get(&kobj->kset->kobj);  
    }  
    /* old object path */  
    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;  
    error = sysfs_move_dir(kobj, new_parent);  
    if (error)  
        goto out;  
    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;  
}  

kobject_move()则是在kobj添加到系统后,想移动到新的parent kobject下所调用的函数。在通知用户空间上,与kobject_rename()调用的是同一操作。

void kobject_del(struct kobject *kobj)  
{  
    if (!kobj)  
        return;  
  
    sysfs_remove_dir(kobj);  
    kobj->state_in_sysfs = 0;  
    kobj_kset_leave(kobj);  
    kobject_put(kobj->parent);  
    kobj->parent = NULL;  
}  

kobject_del()仅仅是把kobj从系统中退出,相对于kobject_add()操作。

/** 
 * kobject_get - increment refcount for object. 
 * @kobj: object. 
 */  
struct kobject *kobject_get(struct kobject *kobj)  
{  
    if (kobj)  
        kref_get(&kobj->kref);  
    return kobj;  
}  
  
/* 
 * kobject_cleanup - free kobject resources. 
 * @kobj: object to cleanup 
 */  
static void kobject_cleanup(struct kobject *kobj)  
{  
    struct kobj_type *t = get_ktype(kobj);  
    const char *name = kobj->name;  
  
    pr_debug("kobject: '%s' (%p): %s\n",  
         kobject_name(kobj), kobj, __func__);  
  
    if (t && !t->release)  
        pr_debug("kobject: '%s' (%p): does not have a release() "  
             "function, it is broken and must be fixed.\n",  
             kobject_name(kobj), kobj);  
  
    /* send "remove" if the caller did not do it but sent "add" */  
    if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {  
        pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",  
             kobject_name(kobj), kobj);  
        kobject_uevent(kobj, KOBJ_REMOVE);  
    }  
  
    /* remove from sysfs if the caller did not do it */  
    if (kobj->state_in_sysfs) {  
        pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",  
             kobject_name(kobj), kobj);  
        kobject_del(kobj);  
    }  
  
    if (t && t->release) {  
        pr_debug("kobject: '%s' (%p): calling ktype release\n",  
             kobject_name(kobj), kobj);  
        t->release(kobj);  
    }  
  
    /* free name if we allocated it */  
    if (name) {  
        pr_debug("kobject: '%s': free name\n", name);  
        kfree(name);  
    }  
}  
  
static void kobject_release(struct kref *kref)  
{  
    kobject_cleanup(container_of(kref, struct kobject, kref));  
}  
  
/** 
 * kobject_put - decrement refcount for object. 
 * @kobj: object. 
 * 
 * Decrement the refcount, and if 0, call kobject_cleanup(). 
 */  
void kobject_put(struct kobject *kobj)  
{  
    if (kobj) {  
        if (!kobj->state_initialized)  
            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);  
    }  
}  

kobject_get()和kobject_put()走的完全是引用计数的路线。kobject_put()会在引用计数降为零时撤销整个kobject的存在:向用户空间发生REMOVE消息,从sysfs中删除相应目录,调用kobj_type中定义的release函数,释放name所占的空间。

看看前面介绍的API。

int kobject_set_name(struct kobject *kobj, const char *name, ...)  
                __attribute__((format(printf, 2, 3)));  
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,  
                  va_list vargs);  
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);  
int __must_check kobject_add(struct kobject *kobj,  
                    struct kobject *parent,  
                    const char *fmt, ...);  
int __must_check kobject_init_and_add(struct kobject *kobj,  
                         struct kobj_type *ktype,  
                         struct kobject *parent,  
                         const char *fmt, ...);  
void kobject_del(struct kobject *kobj);  
  
int __must_check kobject_rename(struct kobject *, const char *new_name);  
int __must_check kobject_move(struct kobject *, struct kobject *);  
  
struct kobject *kobject_get(struct kobject *kobj);  
void kobject_put(struct kobject *kobj);  
  
char *kobject_get_path(struct kobject *kobj, gfp_t flag);  

基本上概扩了kobject从创建到删除,包括中间改名字,改位置,以及引用计数的变动。

当然,kobject创建仍比较麻烦,因为ktype需要自己写。下面就是kobject提供的一种快速创建方法。

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;  
}  
  
struct sysfs_ops kobj_sysfs_ops = {  
    .show   = kobj_attr_show,  
    .store  = kobj_attr_store,  
};  
  
static void dynamic_kobj_release(struct kobject *kobj)  
{  
    pr_debug("kobject: (%p): %s\n", kobj, __func__);  
    kfree(kobj);  
}  
  
static struct kobj_type dynamic_kobj_ktype = {  
    .release    = dynamic_kobj_release,  
    .sysfs_ops  = &kobj_sysfs_ops,  
};  

这个就是kobject自身提供的一种kobj_type,叫做dynamic_kobj_ktype。它没有提供默认的属性,但提供了release函数及访问属性的方法。

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

在kobject_create()及kobject_create_add()中,使用了这种dynamic_kobj_ktype。这是一种很好的偷懒方法。因为release()函数会释放kobj,所以这里的kobj必须是kobject_create()动态创建的。这里的kobject_create()和kobject_init()相对,kobject_create_and_add()和kobject_init_and_add()相对。值得一提的是,这里用kobject_create()和kobject_create_and_add()创建的kobject无法嵌入其它结构,是独立的存在,所以用到的地方很少。

void kset_init(struct kset *k)  
{  
    kobject_init_internal(&k->kobj);  
    INIT_LIST_HEAD(&k->list);  
    spin_lock_init(&k->list_lock);  
}  

kset_init()对kset进行初始化。不过它的界限同kobject差不多。

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()最大的特点是简单,它只负责把kset中的kobject连入系统,并发布KOBJ_ADD消息。所以在调用它之前,你要先设置好k->kobj.name、k->kobj.parent、k->kobj.kset。

void kset_unregister(struct kset *k)  
{  
    if (!k)  
        return;  
    kobject_put(&k->kobj);  
}  

kset_unregister()只是简单地释放创建时获得的引用计数。使用引用计数就是这么简单。

struct kobject *kset_find_obj(struct kset *kset, const char *name)  
{  
    struct kobject *k;  
    struct kobject *ret = NULL;  
  
    spin_lock(&kset->list_lock);  
    list_for_each_entry(k, &kset->list, entry) {  
        if (kobject_name(k) && !strcmp(kobject_name(k), name)) {  
            ret = kobject_get(k);  
            break;  
        }  
    }  
    spin_unlock(&kset->list_lock);  
    return ret;  
}  

kset_find_obj()从kset的链表中找到名为name的kobject。这纯粹是一个对外的API。

static void kset_release(struct kobject *kobj)  
{  
    struct kset *kset = container_of(kobj, struct kset, kobj);  
    pr_debug("kobject: '%s' (%p): %s\n",  
         kobject_name(kobj), kobj, __func__);  
    kfree(kset);  
}  
  
static struct kobj_type kset_ktype = {  
    .sysfs_ops  = &kobj_sysfs_ops,  
    .release = kset_release,  
};  

与kobject相对的,kset也提供了一种kobj_type,叫做kset_ktype。

static struct kset *kset_create(const char *name,  
                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, 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_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,  
                 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;  
}  

kset_create()和kset_create_and_add()就是使用kset_type的快速创建函数。

说实话,使用kobject_create_and_add()的比较少见,但使用 kset_create_and_add()的情形还是见过一些的。比如sysfs中那些顶层的目录,就是单纯的目录,不需要嵌入什么很复杂的结构,用简单的kset_create_and_add()创建就好了。

static inline const char *kobject_name(const struct kobject *kobj)  
{  
    return kobj->name;  
}  
  
static inline struct kset *to_kset(struct kobject *kobj)  
{  
    return kobj ? container_of(kobj, struct kset, kobj) : NULL;  
}  
  
static inline struct kset *kset_get(struct kset *k)  
{  
    return k ? to_kset(kobject_get(&k->kobj)) : NULL;  
}  
  
static inline void kset_put(struct kset *k)  
{  
    kobject_put(&k->kobj);  
}  
  
static inline struct kobj_type *get_ktype(struct kobject *kobj)  
{  
    return kobj->ktype;  
}  

这些是在kobject.h中的内联函数。这里内联函数更多的意思是方便,易于屏蔽内部实现。

以上就是kobject共800余行的代码实现,当然我们忽略了uevent的那部分。

事实证明,自底向上或者顺序的代码分析方法,还是很适合千行左右的代码分析。而且这样分析很全面,容易我们洞察整个模块的意图,从而在理解代码时从较高的抽象角度去看。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  linux 数据结构