Chinaunix首页 | 论坛 | 博客
  • 博客访问: 803777
  • 博文数量: 869
  • 博客积分: 201
  • 博客等级: 入伍新兵
  • 技术积分: 3376
  • 用 户 组: 普通用户
  • 注册时间: 2010-07-21 19:42
文章分类

全部博文(869)

文章存档

2014年(4)

2013年(415)

2012年(453)

我的朋友

分类:

2012-10-19 13:31:15

接着上一篇文章,分析 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 注册到文件系统),另外用输入参数初始化kset uevent_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;

如果如果kobj kset parent 字段都不存在,说明找不到所属kset ,也就没有uevent_ops ,不能产生事件,返回错误信息;相反则找到了存在kset kobj 或父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;

    }

获得引发事件的kobject sysfs 中的路径。

    /* 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_ops uevent 函数,编程人员可在此函数中实现自定义的功能。

    /*

      * 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_ADD KOBJ_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;

添加HOME PATH 环境变量。

        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 则释放其内存空间。

 

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

阅读(212) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~