linux设备驱动模型一三基础结构之Kset

有时候,某个设备的可能具有多个kobject的子类对象,或者某些设备具有相同的特性,为了便于管理,应该把这些对象统一放入一个容器中。这里要用到的容器就是kset。kset只是kobject的一个集合。对应到linux文件系统中,一个kset就是/sys下的一个文件夹。

Kset本身也是一个kobject,所以它在sysfs里同样表现为一个目录,但它和kobject的不同之处在于kset可以看作是一个容器,如果你把它类比为C++里的容器类如list也无不可。Kset之所以能作为容器来使用,其内部正是内嵌了一个双向链表结构struct list_head

kset 象 kobj_type 结构的扩展; 一个 kset 是嵌入到相同类型结构的 kobject 的集合。但 struct kobj_type 关注的是对象的类型,而struct kset 关心的是对象的聚合和集合,其主要功能是包容,可认为是kobjects 的顶层容器类。每个 kset 在内部包含自己的 kobject, 并可以用多种处理kobject 的方法处理kset。 ksets 总是在 sysfs 中出现; 一旦设置了 kset 并把它添加到系统中, 将在 sysfs 中创建一个目录;kobjects 不必在 sysfs 中表示, 但kset中的每一个 kobject 成员都在sysfs中得到表述。

kobject建立的目录下只能添加文件,
kset建立的目录下添加目录,
kset是具有相同类型的kobject的集合。

struct kset:

  1. struct kset {  
  2.     struct list_head list;//连接该kset中所有kobject的链表头     
  3.     spinlock_t list_lock;  
  4.     struct kobject kobj; //内嵌的kobject  
  5.     const struct kset_uevent_ops *uevent_ops;//处理热插拔事件的操作集合  
  6. };  
包含在 kset 中的所有 kobject 被组织成一个双向循环链表, list 域正是该链表的头 Kset 数据结构还内嵌了一个 kobject 对象(由 kobj 域表示),所 有属于这个 kset  kobject 对象的 parent 域均指向这个内嵌的对象。此外, kset 还依赖于 kobj 维护引用计数: kset 的引用计数实际上就是内嵌的  kobject 对象的引用计数
我们在看一下kobject、kset的关系:


从图中我们可以看出,要使一个kobject 和 kset关联,只需要 把它的kset指针指向相应的kSet

Kset的操作与kobject类似,因为kset中内嵌了一个kobject结构,所以,大部份操作都是集中在kset->kobject上.具体分析一下kset_create_and_add()这个接口,类似上面分析的kobject接口,这个接口也包括了kset的大部分操作.代码如下:

  1. struct kset *kset_create_and_add(const char *name,  
  2.                  const struct kset_uevent_ops *uevent_ops,  
  3.                  struct kobject *parent_kobj)  
  4. {  
  5.     struct kset *kset;  
  6.     int error;  
  7.   
  8.     kset = kset_create(name, uevent_ops, parent_kobj);  //创建一个kset  
  9.     if (!kset)  
  10.         return NULL;  
  11.     error = kset_register(kset);//注册kset  
  12.     if (error) {  
  13.         kfree(kset);  
  14.         return NULL;  
  15.     }  
  16.     return kset;  
  17. }  

Kset_create()用来创建一个struct kset结构我们注意,在这里创建kset时.为其内嵌的kobject指定其ktype结构为kset_ktype.这个结构的定义如下:

  1. static struct kset *kset_create(const char *name,  
  2.                 const struct kset_uevent_ops *uevent_ops,  
  3.                 struct kobject *parent_kobj)  
  4. {  
  5.     struct kset *kset;  
  6.     int retval;  
  7.   
  8.     kset = kzalloc(sizeof(*kset), GFP_KERNEL);  
  9.     if (!kset)  
  10.         return NULL;  
  11.     retval = kobject_set_name(&kset->kobj, name);//设置kset的名字,也即内嵌kobject的名字  
  12.     if (retval) {  
  13.         kfree(kset);  
  14.         return NULL;  
  15.     }  
  16.     kset->uevent_ops = uevent_ops;//kset属性操作  
  17.     kset->kobj.parent = parent_kobj;//设置其parent  
  18.   
  19.     /* 
  20.      * The kobject of this kset will have a type of kset_ktype and belong to 
  21.      * no kset itself.  That way we can properly free it when it is 
  22.      * finished being used. 
  23.      */  
  24.     kset->kobj.ktype = &kset_ktype;//ktype指定为kset_ktype  
  25.     kset->kobj.kset = NULL;  
  26.   
  27.     return kset;  
  28. }  

我们注意,在这里创建kset时.为其内嵌的kobject指定其ktype结构为kset_ktype.这个结构的定义如下:

  1. static struct kobj_type kset_ktype = {  
  2.     .sysfs_ops  = &kobj_sysfs_ops,  
  3.     .release = kset_release,  
  4. };  
属性文件的读写操作全部都包含在sysfs_ops成员里.kobj_sysfs_ops的定义如下:

  1. const struct sysfs_ops kobj_sysfs_ops = {  
  2.     .show   = kobj_attr_show,  
  3.     .store  = kobj_attr_store,  
  4. };  
Show,store成员对应的函数代码如下所示:

  1. /* default kobject attribute operations */  
  2. static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,  
  3.                   char *buf)  
  4. {  
  5.     struct kobj_attribute *kattr;  
  6.     ssize_t ret = -EIO;  
  7.   
  8.     kattr = container_of(attr, struct kobj_attribute, attr);  
  9.     if (kattr->show)  
  10.         ret = kattr->show(kobj, kattr, buf);  
  11.     return ret;  
  12. }  
  1. static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,  
  2.                    const char *buf, size_t count)  
  3. {  
  4.     struct kobj_attribute *kattr;  
  5.     ssize_t ret = -EIO;  
  6.   
  7.     kattr = container_of(attr, struct kobj_attribute, attr);  
  8.     if (kattr->store)  
  9.         ret = kattr->store(kobj, kattr, buf, count);  
  10.     return ret;  
  11. }  
从上面的代码看以看出.会将struct attribute结构转换为struct kobj_attribte结构.也就是说struct kobj_attribte内嵌了一个struct attribute.

创建好了kset之后,会调用kset_register().这个函数就是kset操作的核心代码了.如下:

  1. int kset_register(struct kset *k)  
  2. {  
  3.     int err;  
  4.     if (!k)  
  5.         return -EINVAL;  
  6.   
  7.     kset_init(k);//初始化kset  
  8.     err = kobject_add_internal(&k->kobj);//建立空间层次结构  
  9.     if (err)  
  10.         return err;  
  11.     kobject_uevent(&k->kobj, KOBJ_ADD);//通知用户空间的hotplug程序处理  
  12.     return 0;  
  13. }  
kobject_add_internal在Kobject里面已经讲了,主要是建立空间的层次结构,这里主要介绍下kobject_uevent.

kobject_uenent的第二个参数是事件的类型,类型是kobject_action的枚举:

  1. enum kobject_action {  
  2.     KOBJ_ADD,  
  3.     KOBJ_REMOVE,  
  4.     KOBJ_CHANGE,  
  5.     KOBJ_MOVE,  
  6.     KOBJ_ONLINE,  
  7.     KOBJ_OFFLINE,  
  8.     KOBJ_MAX  
  9. };  
kobject_uevent只是简单的调用kobject_uevent_env:

  1. int kobject_uevent(struct kobject *kobj, enum kobject_action action)  
  2. {  
  3.     return kobject_uevent_env(kobj, action, NULL);  
  4. }  
kobject_uevent_env三个参数含义分别为:引起事件的kobject.事件类型(add,remove,change,move,online,offline等).第三个参数是要添加的环境变量. 

  1. int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,  
  2.                char *envp_ext[])  
  3. {  
  4.     struct kobj_uevent_env *env;  
  5.     const char *action_string = kobject_actions[action];  
  6.     const char *devpath = NULL;  
  7.     const char *subsystem;  
  8.     struct kobject *top_kobj;  
  9.     struct kset *kset;  
  10.     const struct kset_uevent_ops *uevent_ops;  
  11.     u64 seq;  
  12.     int i = 0;  
  13.     int retval = 0;  
  14. #ifdef CONFIG_NET  
  15.     struct uevent_sock *ue_sk;  
  16. #endif  
  17.   
  18.     pr_debug("kobject: '%s' (%p): %s\n",  
  19.          kobject_name(kobj), kobj, __func__);  
  20.   
  21.     /*由于对事件的处理函数包含在kobject->kset-> uevent_ops中.要处理事件,就必须要找到上层的一个不为空的kset. 
  22.     如果不存在这样的kset.就退出*/  
  23.     /* search the kset we belong to */  
  24.     top_kobj = kobj;  
  25.     while (!top_kobj->kset && top_kobj->parent)// 由kobject的parent向上查找,直到找到一个kobject包含kset  
  26.         top_kobj = top_kobj->parent;  
  27.   
  28.     if (!top_kobj->kset) {  
  29.         pr_debug("kobject: '%s' (%p): %s: attempted to send uevent "  
  30.              "without kset!\n", kobject_name(kobj), kobj,  
  31.              __func__);  
  32.         return -EINVAL;  
  33.     }  
  34.       
  35.     kset = top_kobj->kset;  
  36.     uevent_ops = kset->uevent_ops;  
  37.   
  38.     /* skip the event, if uevent_suppress is set*/  
  39.     if (kobj->uevent_suppress) {  
  40.         pr_debug("kobject: '%s' (%p): %s: uevent_suppress "  
  41.                  "caused the event to drop!\n",  
  42.                  kobject_name(kobj), kobj, __func__);  
  43.         return 0;  
  44.     }  
  45.     /* skip the event, if the filter returns zero. */  
  46.     if (uevent_ops && uevent_ops->filter)// 如果kset中有filter函数,调用filter函数,看看是否需要过滤uevent消息。  
  47.         if (!uevent_ops->filter(kset, kobj)) {  
  48.             pr_debug("kobject: '%s' (%p): %s: filter function "  
  49.                  "caused the event to drop!\n",  
  50.                  kobject_name(kobj), kobj, __func__);  
  51.             return 0;  
  52.         }  
  53.   
  54.     /* originating subsystem */  
  55.     if (uevent_ops && uevent_ops->name)//如果kset中有name函数,调用name函数得到subsystem的名字;否则,subsystem的名字是kset中kobject的名字  
  56.         subsystem = uevent_ops->name(kset, kobj);  
  57.     else  
  58.         subsystem = kobject_name(&kset->kobj);  
  59.     if (!subsystem) {  
  60.         pr_debug("kobject: '%s' (%p): %s: unset subsystem caused the "  
  61.              "event to drop!\n", kobject_name(kobj), kobj,  
  62.              __func__);  
  63.         return 0;  
  64.     }  
  65.     /* environment buffer */  
  66.     env = kzalloc(sizeof(struct kobj_uevent_env), GFP_KERNEL);// 分配一个kobj_uevent_env,并开始填充env环境变量  
  67.     if (!env)  
  68.         return -ENOMEM;  
  69.   
  70.     /* complete object path */  
  71.     devpath = kobject_get_path(kobj, GFP_KERNEL);  
  72.     if (!devpath) {  
  73.         retval = -ENOENT;  
  74.         goto exit;  
  75.     }  
  76.   
  77.     /* default keys */  
  78.     retval = add_uevent_var(env, "ACTION=%s", action_string);//增加环境变量ACTION=<action name>  
  79.     if (retval)  
  80.         goto exit;  
  81.     retval = add_uevent_var(env, "DEVPATH=%s", devpath);// 增加环境变量DEVPATH=<kobj’s path>  
  82.     if (retval)  
  83.         goto exit;  
  84.     retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);// 增加环境变量SUBSYSTEM=<subsystem name>  
  85.     if (retval)  
  86.         goto exit;  
  87.   
  88.     /* keys passed in from the caller */  
  89.     if (envp_ext) {  
  90.         for (i = 0; envp_ext[i]; i++) {  
  91.             retval = add_uevent_var(env, "%s", envp_ext[i]);// 增加环境变量kobject_uevent_env中参数envp_ext指定的环境变量。  
  92.             if (retval)  
  93.                 goto exit;  
  94.         }  
  95.     }  
  96.     //为调用hotplug设置环境变量  
  97.     /* let the kset specific function add its stuff */  
  98.     if (uevent_ops && uevent_ops->uevent) {  
  99.         retval = uevent_ops->uevent(kset, kobj, env);//调用kset的uevent函数,这个函数会继续填充环境变量。  
  100.         if (retval) {  
  101.             pr_debug("kobject: '%s' (%p): %s: uevent() returned "  
  102.                  "%d\n", kobject_name(kobj), kobj,  
  103.                  __func__, retval);  
  104.             goto exit;  
  105.         }  
  106.     }  
  107.   
  108.     /* 
  109.      * Mark "add" and "remove" events in the object to ensure proper 
  110.      * events to userspace during automatic cleanup. If the object did 
  111.      * send an "add" event, "remove" will automatically generated by 
  112.      * the core, if not already done by the caller. 
  113.      */  
  114.     if (action == KOBJ_ADD)  
  115.         kobj->state_add_uevent_sent = 1;  
  116.     else if (action == KOBJ_REMOVE)  
  117.         kobj->state_remove_uevent_sent = 1;  
  118.   
  119.     /* we will send an event, so request a new sequence number */  
  120.     spin_lock(&sequence_lock);  
  121.     seq = ++uevent_seqnum;  
  122.     spin_unlock(&sequence_lock);  
  123.     retval = add_uevent_var(env, "SEQNUM=%llu", (unsigned long long)seq);// 增加环境变量SEQNUM=<seq>,这里seq是静态变量,每次累加  
  124.     if (retval)  
  125.         goto exit;  
  126.   
  127. #if defined(CONFIG_NET)  
  128.     /* send netlink message */  
  129.     mutex_lock(&uevent_sock_mutex);  
  130.     list_for_each_entry(ue_sk, &uevent_sock_list, list) {  
  131.         struct sock *uevent_sock = ue_sk->sk;  
  132.         struct sk_buff *skb;  
  133.         size_t len;  
  134.   
  135.         /* allocate message with the maximum possible size */  
  136.         len = strlen(action_string) + strlen(devpath) + 2;  
  137.         skb = alloc_skb(len + env->buflen, GFP_KERNEL);  
  138.         if (skb) {  
  139.             char *scratch;  
  140.   
  141.             /* add header */  
  142.             scratch = skb_put(skb, len);  
  143.             sprintf(scratch, "%s@%s", action_string, devpath);  
  144.   
  145.             /* copy keys to our continuous event payload buffer */  
  146.             for (i = 0; i < env->envp_idx; i++) {  
  147.                 len = strlen(env->envp[i]) + 1;  
  148.                 scratch = skb_put(skb, len);  
  149.                 strcpy(scratch, env->envp[i]);  
  150.             }  
  151.   
  152.             NETLINK_CB(skb).dst_group = 1;  
  153.             retval = netlink_broadcast_filtered(uevent_sock, skb,// 调用netlink发送uevent消息  
  154.                                 0, 1, GFP_KERNEL,  
  155.                                 kobj_bcast_filter,  
  156.                                 kobj);  
  157.             /* ENOBUFS should be handled in userspace */  
  158.             if (retval == -ENOBUFS)  
  159.                 retval = 0;  
  160.         } else  
  161.             retval = -ENOMEM;  
  162.     }  
  163.     mutex_unlock(&uevent_sock_mutex);  
  164. #endif  
  165.   
  166.     /* call uevent_helper, usually only enabled during early boot */  
  167.     if (uevent_helper[0] && !kobj_usermode_filter(kobj)) {  
  168.         char *argv [3];  
  169.   
  170.         argv [0] = uevent_helper;  
  171.         argv [1] = (char *)subsystem;  
  172.         argv [2] = NULL;  
  173.         retval = add_uevent_var(env, "HOME=/");  
  174.         if (retval)  
  175.             goto exit;  
  176.         retval = add_uevent_var(env,  
  177.                     "PATH=/sbin:/bin:/usr/sbin:/usr/bin");  
  178.         if (retval)  
  179.             goto exit;  
  180.   
  181.         retval = call_usermodehelper(argv[0], argv,// 调用uevent_helper,最终转换成对用户空间sbin/mdev的调用  
  182.                          env->envp, UMH_WAIT_EXEC);  
  183.     }  
  184.   
  185. exit:  
  186.     kfree(devpath);  
  187.     kfree(env);  
  188.     return retval;  
  189. }  

另外一个操作就是将kobject添加到kset,如何将一个kobject添加到kset?

先把kobject的kset成员指向目的kset,即包含这个kobject的kset;然后在前面 分析kobject_add的时候有调用kobject_add(kobj) --.....-> kobj_kset_join(kobj), kobj_kest_join函数的作用就是把kobj自身加入到自身的kset的链表里面。

  1. /* add the kobject to its kset's list */  
  2. static void kobj_kset_join(struct kobject *kobj)  
  3. {  
  4.     if (!kobj->kset)  
  5.         return;  
  6.   
  7.     kset_get(kobj->kset);//增加引用计数  
  8.     spin_lock(&kobj->kset->list_lock);  
  9.     list_add_tail(&kobj->entry, &kobj->kset->list);//添加到kset链表尾  
  10.     spin_unlock(&kobj->kset->list_lock);  
  11. }  

到这里,kset的基本操作也是完了.

最后来看下kset调用流程图:

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值