首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 操作系统 > UNIXLINUX >

Linux装置模型分析之kset(基于3.10.1内核)

2013-10-29 
Linux设备模型分析之kset(基于3.10.1内核)作者:刘昊昱 博客:http://blog.csdn.net/liuhaoyutz内核版本:3.1

Linux设备模型分析之kset(基于3.10.1内核)

作者:刘昊昱 

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

内核版本:3.10.1

 
一、kset结构定义
kset结构体定义在include/linux/kobject.h文件中,其内容如下:
121/**122 * kobject_uevent_env - send an uevent with environmental data123 *124 * @action: action that is happening125 * @kobj: struct kobject that the action is happening to126 * @envp_ext: pointer to environmental data127 *128 * Returns 0 if kobject_uevent_env() is completed with success or the129 * corresponding error when it fails.130 */131int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,132               char *envp_ext[])133{134    struct kobj_uevent_env *env;135    const char *action_string = kobject_actions[action];136    const char *devpath = NULL;137    const char *subsystem;138    struct kobject *top_kobj;139    struct kset *kset;140    const struct kset_uevent_ops *uevent_ops;141    int i = 0;142    int retval = 0;143#ifdef CONFIG_NET144    struct uevent_sock *ue_sk;145#endif146147    pr_debug("kobject: '%s' (%p): %s\n",148         kobject_name(kobj), kobj, __func__);149150    /* search the kset we belong to */151    top_kobj = kobj;152    while (!top_kobj->kset && top_kobj->parent)153        top_kobj = top_kobj->parent;154155    if (!top_kobj->kset) {156        pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "157             "without kset!\n", kobject_name(kobj), kobj,158             __func__);159        return -EINVAL;160    }161162    kset = top_kobj->kset;163    uevent_ops = kset->uevent_ops;164165    /* skip the event, if uevent_suppress is set*/166    if (kobj->uevent_suppress) {167        pr_debug("kobject: '%s' (%p): %s: uevent_suppress "168                 "caused the event to drop!\n",169                 kobject_name(kobj), kobj, __func__);170        return 0;171    }172    /* skip the event, if the filter returns zero. */173    if (uevent_ops && uevent_ops->filter)174        if (!uevent_ops->filter(kset, kobj)) {175            pr_debug("kobject: '%s' (%p): %s: filter function "176                 "caused the event to drop!\n",177                 kobject_name(kobj), kobj, __func__);178            return 0;179        }180181    /* originating subsystem */182    if (uevent_ops && uevent_ops->name)183        subsystem = uevent_ops->name(kset, kobj);184    else185        subsystem = kobject_name(&kset->kobj);186    if (!subsystem) {187        pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "188             "event to drop!\n", kobject_name(kobj), kobj,189             __func__);190        return 0;191    }192193    /* environment buffer */194    env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);195    if (!env)196        return -ENOMEM;197198    /* complete object path */199    devpath = kobject_get_path(kobj, GFP_KERNEL);200    if (!devpath) {201        retval = -ENOENT;202        goto exit;203    }204205    /* default keys */206    retval = add_uevent_var(env, "ACTION=%s", action_string);207    if (retval)208        goto exit;209    retval = add_uevent_var(env, "DEVPATH=%s", devpath);210    if (retval)211        goto exit;212    retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);213    if (retval)214        goto exit;215216    /* keys passed in from the caller */217    if (envp_ext) {218        for (i = 0; envp_ext[i]; i++) {219            retval = add_uevent_var(env, "%s", envp_ext[i]);220            if (retval)221                goto exit;222        }223    }224225    /* let the kset specific function add its stuff */226    if (uevent_ops && uevent_ops->uevent) {227        retval = uevent_ops->uevent(kset, kobj, env);228        if (retval) {229            pr_debug("kobject: '%s' (%p): %s: uevent() returned "230                 "%d\n", kobject_name(kobj), kobj,231                 __func__, retval);232            goto exit;233        }234    }235236    /*237     * Mark "add" and "remove" events in the object to ensure proper238     * events to userspace during automatic cleanup. If the object did239     * send an "add" event, "remove" will automatically generated by240     * the core, if not already done by the caller.241     */242    if (action == KOBJ_ADD)243        kobj->state_add_uevent_sent = 1;244    else if (action == KOBJ_REMOVE)245        kobj->state_remove_uevent_sent = 1;246247    mutex_lock(&uevent_sock_mutex);248    /* we will send an event, so request a new sequence number */249    retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)++uevent_seqnum);250    if (retval) {251        mutex_unlock(&uevent_sock_mutex);252        goto exit;253    }254255#if defined(CONFIG_NET)256    /* send netlink message */257    list_for_each_entry(ue_sk, &uevent_sock_list, list) {258        struct sock *uevent_sock = ue_sk->sk;259        struct sk_buff *skb;260        size_t len;261262        if (!netlink_has_listeners(uevent_sock, 1))263            continue;264265        /* allocate message with the maximum possible size */266        len = strlen(action_string) + strlen(devpath) + 2;267        skb = alloc_skb(len + env->buflen, GFP_KERNEL);268        if (skb) {269            char *scratch;270271            /* add header */272            scratch = skb_put(skb, len);273            sprintf(scratch, "%s@%s", action_string, devpath);274275            /* copy keys to our continuous event payload buffer */276            for (i = 0; i < env->envp_idx; i++) {277                len = strlen(env->envp[i]) + 1;278                scratch = skb_put(skb, len);279                strcpy(scratch, env->envp[i]);280            }281282            NETLINK_CB(skb).dst_group = 1;283            retval = netlink_broadcast_filtered(uevent_sock, skb,284                                0, 1, GFP_KERNEL,285                                kobj_bcast_filter,286                                kobj);287            /* ENOBUFS should be handled in userspace */288            if (retval == -ENOBUFS || retval == -ESRCH)289                retval = 0;290        } else291            retval = -ENOMEM;292    }293#endif294    mutex_unlock(&uevent_sock_mutex);295296    /* call uevent_helper, usually only enabled during early boot */297    if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {298        char *argv [3];299300        argv [0] = uevent_helper;301        argv [1] = (char *)subsystem;302        argv [2] = NULL;303        retval = add_uevent_var(env, "HOME=/");304        if (retval)305            goto exit;306        retval = add_uevent_var(env,307                    "PATH=/sbin:/bin:/usr/sbin:/usr/bin");308        if (retval)309            goto exit;310311        retval = call_usermodehelper(argv[0], argv,312                         env->envp, UMH_WAIT_EXEC);313    }314315exit:316    kfree(devpath);317    kfree(env);318    return retval;319}


122行,从注释可以看出,kobject_uevent_env函数的作用是发送带有环境变量数据的uevent。
150-160行,查找kobject所属的kset,如果这个kobject没有所属的kset,则看这个kobject.parent有没有所属的kset,如果还没有,继续沿着kobject层次结构树向上查找,直到找到一个具有所属kset的祖先kobject,如果确实没有找到,则出错退出。所以当前kobject的层次结构树中,必须有一个具有所属的kset。因为对事件的处理函数包含在kobject.kset.uevent_ops中,要处理事件,就必须找到上层一个不为空的kset。
值得注意的是,在创建kset的过程中,kset_create_and_add->kset_create,在kset_create函数中,将kset.kobj.kset设置为NULL,所以kset.kobj本身没有所属的kset,但是同样在kset_create函数中,kset.kobj.parent设置为parent_kobj,所以kset.kobj必然通过其上层祖先查找kset。
162行,取得相应的kset。
163行,将kset.uevent_ops赋值给uevent_ops变量。
165-171行,如果kobj->uevent_suppress被设置为1,则不发送uevent,退出。
172-179行,如果uevent_ops->filter(kset, kobj)返回值为0,说明kobj希望发送的uevent被顶层kset过滤掉了,不再发送。
181-191行,通过uevent_ops->name函数取得子系统名,如果uevent_ops->name为NULL,则使用kset.kobj.name做为子系统名。
194行,分配struct kobj_uevent_env变量空间给env,该结构体用来保存环境变量,它定义在include/linux/kobject.h文件中,其内容如下:
116struct kobj_uevent_env {
117    char *envp[UEVENT_NUM_ENVP];
118    int envp_idx;
119    char buf[UEVENT_BUFFER_SIZE];
120    int buflen;
121};
199行,调用kobject_get_path取得kobject的绝对路径。
205-214行,调用add_uevent_var函数将ACTION、DEVPATH、SUBSYSTEM三个默认环境变量添加到env中。add_uevent_var函数定义在lib/kobject_uevent.c文件中,其作用是“add key value string to the environment buffer”。
217-223行,如果调用kobject_uevent_env函数时,通过第三个参数envp_ext传递进来了其它相关环境变量,也通过add_uevent_var函数添加到env中。
225-234行,如果uevent_ops->uevent不为空,则调用uevent_ops->uevent,kset可以通过该函数完成自己特定的功能。
236-246行,如果action是KOBJ_ADD,则设置kobj->state_add_uevent_sent为1。如果action是KOBJ_REMOVE,则设置kobj->state_remove_uevent_sent为1。其作用注释中说的很清楚“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.”。
249行,将SEQNUM环境变量添加到env中。
kobject_uevent_env函数剩下的部分,用来和用户空间进程进行交互(或者在内核空间启动执行一个用户空间程序)。在Linux中,有两种方式完成这种交互,一个是代码中由CONFIG_NET宏包含的部分,即255-293行,这部分代码通过udev的方式向用户空间广播当前kset对象中的uevent事件。另外一种方式是在内核空间启动一个用户空间进程/sbin/hotplug,通过给该进程传递内核设定的环境变量的方式来通知用户空间kset对象中的uevent事件,即代码中296-312行。
热插拔(hotplug)是指当有设备插入或拨出系统时,内核可以检测到这种状态变化,并通知用户空间加载或移除该设备对应的驱动程序模块。在Linux系统上内核有两种机制可以通知用户空间执行加载或移除操作,一种是udev,另一种是/sbin/hotplug,在Linux发展的早期,只有/sbin/hotplug,它的幕后推手是内核中的call_usermodehelper函数,它能从内核空间启动一个用户空间程序。随着内核的发展,出现了udev机制并逐渐取代了/sbin/hotplug。udev的实现基于内核中的网络机制,它通过创建标准的socket接口来监听来自内核的网络广播包,并对接收到的包进行分析处理。
至此,kobject_uevent_env函数我们就分析完了,同时,kobject_uevent、kset_register、kset_create_and_add函数也分析完了,我们了解了kset的创建和注册过程。

热点排行