Linux设备模型分析之kobject

作者:刘昊昱 

博客:http://blog.csdn.net/liuhaoyutz

内核版本:2.6.36

 
一、kobject应用举例
Linux设备模型最基本的组成元素是kobject,我们先来看一个kobject的应用例子,该程序在Ubuntu 10.10, 2.6.32-38-generic-pae内核上调试通过。
 
  
#include 
#include 
#include 
#include 
#include 
#include 
#include 

MODULE_AUTHOR("haoyu");
MODULE_LICENSE("Dual BSD/GPL");

struct my_kobject
{
    int value;
    struct kobject kobj;
};
    
struct my_kobject my_kobj;

void kobject_release(struct kobject *kobject);
ssize_t kobject_attr_show(struct kobject *kobject, struct attribute *attr,char *buf);
ssize_t kobject_attr_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);

struct attribute kobject_attr1 = {
    .name = "name",
    .mode = S_IRWXUGO,
};

struct attribute kobject_attr2 = {
    .name = "value",
    .mode = S_IRWXUGO,
};

static struct attribute *kobject_def_attrs[] = {
    &kobject_attr1,
    &kobject_attr2,
    NULL,
};

struct sysfs_ops kobject_sysfs_ops =
{
    .show = kobject_attr_show,
    .store = kobject_attr_store,
};

struct kobj_type ktype =
{
    .release = kobject_release,
    .sysfs_ops = &kobject_sysfs_ops,
    .default_attrs = kobject_def_attrs,
};

void kobject_release(struct kobject *kobject)
{
    printk("kobject release.\n");
}

ssize_t kobject_attr_show(struct kobject *kobject, struct attribute *attr,char *buf)
{
    int count = 0;
    struct my_kobject *my_kobj = container_of(kobject, struct my_kobject, kobj);
    printk("kobject attribute show.\n");
    if(strcmp(attr->name, "name") == 0)
        count = sprintf(buf, "%s\n", kobject->name);
    else if(strcmp(attr->name, "value") == 0)
        count = sprintf(buf, "%d\n", my_kobj->value);
    else
        printk("no this attribute.\n");
    
    return count;
}

ssize_t kobject_attr_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count)
{
    int val;
    struct my_kobject *my_kobj = container_of(kobject, struct my_kobject, kobj);
    printk("kobject attribute store.\n");
    if(strcmp(attr->name, "name") == 0)
        printk("Can not change name.\n");
    else if(strcmp(attr->name, "value") == 0)
    {
        val = buf[0] - '0';
        if(val == 0 || val == 1)
            my_kobj->value = val;
        else
            printk("value is '0' or '1'\n");
    }
    else
        printk("no this attribute.\n");
        
    return count;
}

static int kobject_test_init(void)
{
    printk("kboject test init.\n");
    kobject_init_and_add(&my_kobj.kobj,&ktype,NULL,"kobject_test");
    return 0;
}

static void kobject_test_exit(void)
{
    printk("kobject test exit.\n");
    kobject_del(&my_kobj.kobj);
}

module_init(kobject_test_init);
module_exit(kobject_test_exit);

该模块执行过程如下图所示:
 
 
  
二、相关数据结构:
kobject是Linux设备模型中最基本的数据结构,代表设备模式的一个基本对象。
kobj_type是kobject的类型,包括kobject的属性以及属性的操作接口,不同的kobject可以具有相同的kobj_type。
kset是几个kobject的集合,这些kobject可以具有相同的kobj_type,也可以具有不同的kobj_type。
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 kset - a set of kobjects of a specific type, belonging to a specific subsystem.
 *
 * A kset defines a group of kobjects.  They can be individually
 * different "types" but overall these kobjects all want to be grouped
 * together and operated on in the same manner.  ksets are used to
 * define the attribute callbacks and other common events that happen to
 * a kobject.
 *
 * @list: the list of all kobjects for this kset
 * @list_lock: a lock for iterating over the kobjects
 * @kobj: the embedded kobject for this kset (recursion, isn't it fun...)
 * @uevent_ops: the set of uevent operations for this kset.  These are
 * called whenever a kobject has something happen to it so that the kset
 * can add new environment variables, or filter out the uevents if so
 * desired.
 */
struct kset {
        struct list_head list;
        spinlock_t list_lock;
        struct kobject kobj;
        const struct kset_uevent_ops *uevent_ops;
};
 
struct kset_uevent_ops {
        int (* const filter)(struct kset *kset, struct kobject *kobj);
        const char *(* const name)(struct kset *kset, struct kobject *kobj);
        int (* const uevent)(struct kset *kset, struct kobject *kobj,
                     struct kobj_uevent_env *env);
};
 
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);
};
 
struct sysfs_ops {
        ssize_t (*show)(struct kobject *, struct attribute *,char *);
        ssize_t (*store)(struct kobject *,struct attribute *,const char *, size_t);
};
 
struct attribute {
        const char             *name;
        mode_t                 mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
        struct lock_class_key  *key;
        struct lock_class_key  skey;
#endif
};
 
/*
 * Callbacks so sysfs can determine namespaces
 *   @current_ns: return calling task's namespace
 *   @netlink_ns: return namespace to which a sock belongs (right?)
 *   @initial_ns: return the initial namespace (i.e. init_net_ns)
 */
struct kobj_ns_type_operations {
        enum kobj_ns_type type;
        const void *(*current_ns)(void);
        const void *(*netlink_ns)(struct sock *sk);
        const void *(*initial_ns)(void);
};
 
struct kref {
        atomic_t refcount;
};

 
三、kobject注册和注销过程分析
kobject的注册是通过调用kobject_init_and_add函数,该函数定义如下:
/**
 * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
 * @kobj: pointer to the kobject to initialize
 * @ktype: pointer to the ktype for this kobject.
 * @parent: pointer to the parent of this kobject.
 * @fmt: the name of the kobject.
 *
 * This function combines the call to kobject_init() and
 * kobject_add().  The same type of error handling after a call to
 * kobject_add() and kobject lifetime rules are the same here.
 */
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函数对kobject对象进行基本的初始化。然后,调用kobject_add_varg函数将kobject注册到系统中。va_start和va_end是处理可变参数的固定语法。
先来看kobject_init,该函数定义如下:
/**
 * 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();
}

该函数首先确保kobj和ktype都存在,否则直接退出。如果该kobj进行过初始化,则打印警告信息。然后调用kobject_init_internal真正开始初始化kobj,最后把kobj->ktype设置为ktype。
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;
}

首先初始化kobj->kref,实际上kobj->kref就是一个原子变量(atomic_t)。接着初始化链表项kobj->entry,并设置其他kobject成员。
至此,kobject_init函数就分析完了,我们返回到kobject_init_and_add函数,下面该分析kobject_add_varg函数了:
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_set_name_vargs设置kob->name。然后初始化kobj->parent为parent参数指定的kobject。最后,调用kobject_add_internal将kobject注册到系统中,该函数定义如下:
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) : "",
                kobj->kset ? kobject_name(&kobj->kset->kobj) : "");
 
        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;
}

首先确保kobj->name已经被赋值,即kobject必须有名字。如果指定了kobj->kset,则调用kobj_kset_join将kobj加入到kobj->kset中。同时,如果kobj->parent仍为NULL,则将kobj->parent设置为kobj->kset->kobj。
然后,调用create_dir(kobj)在/sys目录下建立kobject相关目录结构。
kobj_kset_join函数定义如下:
/* 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);
}

create_dir函数定义如下:
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;
}

首先调用sysfs_create_dir在/sys下建立目录,然后再调用populate_dir在新建目录下生成属性文件。
sysfs_create_dir函数定义如下:
/**
 *      sysfs_create_dir - create a directory for an object.
 *      @kobj:         object we're creating directory for. 
 */
int sysfs_create_dir(struct kobject * kobj)
{
        enum kobj_ns_type type;
        struct sysfs_dirent *parent_sd, *sd;
        const void *ns = NULL;
        int error = 0;
 
        BUG_ON(!kobj);
 
        if (kobj->parent)
               parent_sd = kobj->parent->sd;
        else
               parent_sd = &sysfs_root;
 
        if (sysfs_ns_type(parent_sd))
               ns = kobj->ktype->namespace(kobj);
        type = sysfs_read_ns_type(kobj);
 
        error = create_dir(kobj, parent_sd, type, ns, kobject_name(kobj), &sd);
        if (!error)
               kobj->sd = sd;
        return error;
}

这里主要是通过调用create_dir函数建立kobject对应的目录。这个函数我们就不继续向下跟踪了。
下面来看populate_dir函数,其定义如下:
/*
 * populate_dir - populate directory with attributes.
 * @kobj: object we're working on.
 *
 * Most subsystems have a set of default attributes that are associated
 * with an object that registers with them.  This is a helper called during
 * object registration that loops through the default attributes of the
 * subsystem and creates attributes files for them in sysfs.
 */
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;
}

该函数循环遍历kobject的所有属性,并调用sysfs_create_file函数在/sys系统对应目录下建立属性文件。
至此,kobject的注册过程我们就分析完了。
kobject的注销过程是调用kobject_del函数,该函数定义如下:
/**
 * kobject_del - unlink kobject from hierarchy.
 * @kobj: object.
 */
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;
}

这里需要注意的是,只要把目录删除,目录下的属性文件自动就删除了。

你可能感兴趣的:(Linux设备模型)