研究UEVENT相关东西,看到2篇优秀的博文,转载与此

http://blog.chinaunix.net/u3/92745/showart_2145668.html
LINUX设备驱动之设备模型一--kobject

LINUX设备驱动驱动程序模型的核心数据结构是kobjectkobject数据结构在\linux\kobject.h中定义: 

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;

};

每个kobject都有它的父节点parentksetkobj_type指针,这三者是驱动模型的基本结构,ksetkobject的集合,在\linux\kobject.h中定义:

struct kset {

       struct list_head list;

       spinlock_t list_lock;

       struct kobject kobj;

       struct kset_uevent_ops *uevent_ops;

};

可以看到每个kset内嵌了一个kobjectkobj字段),用来表示其自身节点,其list字段指向了所包含的kobject的链表头。我们在后面的分析中将看到kobject如果没有指定父节点,parent将指向其kset内嵌的kobject

每个kobject都有它的kobj_type字段指针,用来表示kobject在文件系统中的操作方法,kobj_type结构也在\linux\kobject.h中定义:

struct kobj_type {

       void (*release)(struct kobject *kobj);

       struct sysfs_ops *sysfs_ops;

       struct attribute ** default_attrs;

};

release方法是在kobject释放是调用,sysfs_ops指向kobject对应的文件操作,default_attrskobject的默认属性,sysfs_ops的将使用default_attrs属性(在后面的分析中我们将会看到)。

从上面的分析我们可以想象到kobjectksetkobj_type的层次结构:

研究UEVENT相关东西,看到2篇优秀的博文,转载与此 - tianbuhuihei - Brina的博客500)this.width=500;" width="500" border="0">

我们可以把一个kobject添加到文件系统中去(实际上是添加到其父节点所代表的kset中去),内核提供kobject_create_and_add()接口函数:

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分配内存空间并对其初始化。

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;

}

kobject_init()kobject基本字段进行初始化,用输入参数设置kobj_type属性。

这里粘出代码以供参考:

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

}

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()函数:

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;

}

在上面的初始化中已把位变量设位1

va_start(args, fmt)va_end(args)使用可变参数(可见参数用法不在这里分析),在kobject_add_varg中将把fmt指向的内容赋给kobjectname字段。下面我们详细看看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(kobj, fmt, vargs),如果kobjname字段指向的内容为空,则为分配一个内存空间并用fmt指向的内容初始化,把地址赋给kobjname字段。

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;

}

char *kvasprintf(gfp_t gfp, const char *fmt, va_list ap)

{

       unsigned int len;

       char *p;

       va_list aq;

 

       va_copy(aq, ap);

       len = vsnprintf(NULL, 0, fmt, aq);

       va_end(aq);

 

       p = kmalloc(len+1, gfp);

       if (!p)

              return NULL;

 

       vsnprintf(p, len+1, fmt, ap);

 

       return p;

}

继续kobject_add_varg()返回kobject_add_internal(kobj),就是在这个函数理为kobj创建文件系统结构:

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;

       }

检查kobj和它的name字段,不存在则返回错误信息。

 

       parent = kobject_get(kobj->parent);

获得其父节点,并增加父节点的计数器,kobject结构中的kref字段用于容器的计数,kobject_getkobject_put分别增加和减少计数器,如果计数器为0,则释放该kobjectkobject_get返回该kobject

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

       }

在这里我们可以看到,如果调用kobject_create_and_add()时参数parent设为NULL,则会去检查kobjkset是否存在,如果存在就会把kset所嵌套的kobj作为其父节点,并把kobj添加到kset中去。

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

打印一些调试信息,接着为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;

}

如果创建不成功,则回滚上面的操作,成功的话则设置kobjstate_in_sysfs标志。

在看看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()先为kobj创建了一个目录文件

int sysfs_create_dir(struct kobject * kobj)

{

       struct sysfs_dirent *parent_sd, *sd;

       int error = 0;

 

       BUG_ON(!kobj);

 

       if (kobj->parent)

              parent_sd = kobj->parent->sd;

       else

              parent_sd = &sysfs_root;

 

       error = create_dir(kobj, parent_sd, kobject_name(kobj), &sd);

       if (!error)

              kobj->sd = sd;

       return error;

}

如果kobj->parentNULL,就把&sysfs_root作为父节点sd,即/sys下面创建结点。

然后调用populate_dir

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;

}

得到kobjkobj_type,历遍kobj_typedefault_attrs并创建属性文件,文件的操作会回溯到sysfs_opsshowstore会调用封装了attributekobj_attribute结构的storeshow方法(在后面的代码中将会分析)。

由于上面kobject_init(kobj, &dynamic_kobj_ktype)用默认dynamic_kobj_ktype作为kobj_type参数,而dynamic_kobj_ktypedefault_attrsNULL,所以这里没有创建属性文件。

至此,我们已经知道了kobject_create_and_add()函数创建kobject,挂到父kobject,并设置其kobj_type,在文件系统中为其创建目录和属性文件等。

另外,如果我们已静态定义了要创建的kobject,则可以调用kobject_init_and_add()来注册kobject,其函数如下:

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的函数是kobject_del()

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;

}

删除kobj目录及其目录下的属性文件,清kobjstate_in_sysfs标志,把kobjkset中删除,减少kobj->parent的计数并设其指针为空。

 


LINUX设备驱动之设备模型二--kset

我们已经知道了kset内嵌了kobject来表示自身的节点,创建kset就要完成其内嵌kobject,注册kset时会产生一个事件,事件而最终会调用uevent_ops字段指向结构中的函数,这个事件是通过用户空间的hotplug程序处理。下面我们一步一步分析。

内核同样提供了创建和注册kset的函数kset_create_and_add()

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_uevent_ops类型的结构变量,其结构包含三个函数指针,我们在后面的分析到这三个函数在什么时候被调用,kset_uevent_ops结构定义如下:

struct kset_uevent_ops {

    int (*filter)(struct kset *kset, struct kobject *kobj);

    const char *(*name)(struct kset *kset, struct kobject *kobj);

    int (*uevent)(struct kset *kset, struct kobject *kobj,

              struct kobj_uevent_env *env);

};

继续看上面的函数,先调用kset_create ()创建一个kset,接着调用kset_register()注册它。

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分配内存,如我们上面分析,初始化了kset内嵌的kobject(这里还未将kobject注册到文件系统),另外用输入参数初始化ksetuevent_ops字段。

接着看kset的注册函数kset_register()

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;

}

在这里终于看到调用kobject_add_internal()将kset内嵌的kobject注册到文件系统,这个函数我们在上面已经分析。

我们上面说到注册kset会产生一个事件,就是在这里调用了kobject_uevent(&k->kobj, KOBJ_ADD)

kobject_uevent()\lib\ kobject_uevent.c中:

int kobject_uevent(struct kobject *kobj, enum kobject_action action)

{

    return kobject_uevent_env(kobj, action, NULL);

}

转入kobject_uevent_env()

这个函数比较长,我们分段分析

int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,

               char *envp_ext[])

{

    struct kobj_uevent_env *env;

    const char *action_string = kobject_actions[action];

    const char *devpath = NULL;

    const char *subsystem;

    struct kobject *top_kobj;

    struct kset *kset;

    struct kset_uevent_ops *uevent_ops;

    u64 seq;

    int i = 0;

    int retval = 0;

 

    pr_debug("kobject: '%s' (%p): %s\n",

         kobject_name(kobj), kobj, __func__);

 

    /* search the kset we belong to */

    top_kobj = kobj;

    while (!top_kobj->kset && top_kobj-> parent)

        top_kobj = top_kobj->parent;

 

    if (!top_kobj->kset) {

        pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "

             "without kset!\n", kobject_name(kobj), kobj,

             __func__);

        return -EINVAL;

    }

 

    kset = top_kobj->kset;

    uevent_ops = kset-> uevent_ops;

如果如果kobjksetparent字段都不存在,说明找不到所属kset,也就没有uevent_ops,不能产生事件,返回错误信息;相反则找到了存在ksetkobj或父kobject(依次往上找),并赋值给uevent_ops

 

    /* skip the event, if uevent_suppress is set*/

    if (kobj-> uevent_suppress) {

        pr_debug("kobject: '%s' (%p): %s: uevent_suppress "

                 "caused the event to drop!\n",

                 kobject_name(kobj), kobj, __func__);

        return 0;

    }

如果设置了uevent_suppress字段,说明不希望产生事件,忽略事件正确返回。注意驱动程序将在适当的地方产生改事件。

    /* skip the event, if the filter returns zero. */

    if (uevent_ops && uevent_ops->filter)

        if (!uevent_ops->filter(kset, kobj)) {

            pr_debug("kobject: '%s' (%p): %s: filter function "

                 "caused the event to drop!\n",

                 kobject_name(kobj), kobj, __func__);

            return 0;

        }

如果uevent_ops->filter返回0,同样忽略事件正确返回。

    if (uevent_ops && uevent_ops->name)

        subsystem = uevent_ops->name(kset, kobj);

    else

        subsystem = kobject_name(&kset->kobj);

    if (!subsystem) {

        pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "

             "event to drop!\n", kobject_name(kobj), kobj,

             __func__);

        return 0;

    }

获得子系统的名称,不存在则返回。

    /* environment buffer */

    env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);

    if (!env)

        return -ENOMEM;

分配一个kobj_uevent_env结构内存,用于存放环境变量的值。

/* complete object path */

    devpath = kobject_get_path(kobj, GFP_KERNEL);

    if (!devpath) {

        retval = -ENOENT;

        goto exit;

    }

获得引发事件的kobjectsysfs中的路径。

    /* default keys */

    retval = add_uevent_var(env, "ACTION=%s", action_string);

    if (retval)

        goto exit;

    retval = add_uevent_var(env, "DEVPATH=%s", devpath);

    if (retval)

        goto exit;

    retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);

    if (retval)

        goto exit;

 

    /* keys passed in from the caller */

    if (envp_ext) {

        for (i = 0; envp_ext[i]; i++) {

            retval = add_uevent_var(env, "%s", envp_ext[i]);

            if (retval)

                goto exit;

        }

    }

调用add_uevent_var()kobj_uevent_env填充action_string,kobject路径,子系统名称以及其他指定环境变量。

 

     /* let the kset specific function add its stuff */

     if (uevent_ops && uevent_ops->uevent) {

         retval = uevent_ops->uevent(kset, kobj, env);

         if (retval) {

              pr_debug("kobject: '%s' (%p): %s: uevent() returned "

                    "%d\n", kobject_name(kobj), kobj,

                    __FUNCTION__, retval);

              goto exit;

         }

     }

调用uevent_opsuevent函数,编程人员可在此函数中实现自定义的功能。

    /*

     * Mark "add" and "remove" events in the object to ensure proper

     * events to userspace during automatic cleanup. If the object did

     * send an "add" event, "remove" will automatically generated by

     * the core, if not already done by the caller.

     */

    if (action == KOBJ_ADD)

        kobj->state_add_uevent_sent = 1;

    else if (action == KOBJ_REMOVE)

        kobj->state_remove_uevent_sent = 1;

设置KOBJ_ADDKOBJ_REMOVE的标志。

    /* we will send an event, so request a new sequence number */

    spin_lock(&sequence_lock);

    seq = ++uevent_seqnum;

    spin_unlock(&sequence_lock);

    retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);

    if (retval)

        goto exit;

 

#if defined(CONFIG_NET)

    /* send netlink message */

    if (uevent_sock) {

        struct sk_buff *skb;

        size_t len;

 

        /* allocate message with the maximum possible size */

        len = strlen(action_string) + strlen(devpath) + 2;

        skb = alloc_skb(len + env->buflen, GFP_KERNEL);

        if (skb) {

            char *scratch;

 

            /* add header */

            scratch = skb_put(skb, len);

            sprintf(scratch, "%s@%s", action_string, devpath);

 

            /* copy keys to our continuous event payload buffer */

            for (i = 0; i < env->envp_idx; i++) {

                len = strlen(env->envp[i]) + 1;

                scratch = skb_put(skb, len);

                strcpy(scratch, env->envp[i]);

            }

 

            NETLINK_CB(skb).dst_group = 1;

            retval = netlink_broadcast(uevent_sock, skb, 0, 1,

                           GFP_KERNEL);

            /* ENOBUFS should be handled in userspace */

            if (retval == -ENOBUFS)

                retval = 0;

        } else

            retval = -ENOMEM;

    }

#endif

    /* call uevent_helper, usually only enabled during early boot */

    if (uevent_helper[0]) {

        char *argv [3];

 

        argv [0] = uevent_helper;

        argv [1] = (char *)subsystem;

        argv [2] = NULL;

        retval = add_uevent_var(env, "HOME=/");

        if (retval)

            goto exit;

        retval = add_uevent_var(env,

                    "PATH=/sbin:/bin:/usr/sbin:/usr/bin");

        if (retval)

            goto exit;

添加HOMEPATH环境变量。

        retval = call_usermodehelper(argv[0], argv,

                         env->envp, UMH_WAIT_EXEC);

    }

 

exit:

    kfree(devpath);

    kfree(env);

    return retval;

}

调用hotplug函数。

看一下kset_unregister()

void kset_unregister (struct kset *k)

{

    if (!k)

        return;

    kobject_put(&k-> kobj);

}

减少其内嵌的kobj计数,为0则释放其内存空间。

 

已经分析完kobjectksetlinux的设备模型就是基于这两个数据结构的,在此基础上,后续将分析设备模型中的devicedriver、和bus

posted @ 2010-12-24 17:33  天不会黑  阅读(1265)  评论(0编辑  收藏  举报