zoukankan      html  css  js  c++  java
  • 研究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

    天不会黑
  • 相关阅读:
    iOS截取http/https流量
    Jenkins拾遗--第五篇-git插件填坑
    Jenkins拾遗--第三篇(用户权限管理)
    Jenkins拾遗--第四篇-适当的让构建失败
    Jenkins拾遗--第二篇(初步配置Jenkins)
    Jenkins拾遗--第一篇(安装Jenkins)
    一个测试人员的2015的回顾和2016年展望
    xcode升级导致git无法使用
    在intellj idea下用sbt的坑
    本人已转战知乎,此处不玩了。
  • 原文地址:https://www.cnblogs.com/yuanfang/p/1916229.html
Copyright © 2011-2022 走看看