Device Module之Kobject,Ktype,Kset(基于kernel 4.11)

来源:互联网 发布:java模糊查询 编辑:程序博客网 时间:2024/06/01 18:45

笔者:从最初的希望很快结束NET,INTERRUPT subsystem 到现在才发现,仅仅是NET都是涉及内容深广,且不说

相关协议的内容,仅仅是框架也是十分复杂的,最近又在看USB子系统,与裸板驱动USB controller的区别就是框架,

与其说是框架,不如说是Linux大牛们,当初关于USB,PCI,IIC等总线设备的想出的解决方案,我查阅了大量资料,不论

英文还是中文,但是如果想真正去理解,还必须自己去解析一次,自己去梳理下代码,别人做的永远是别人做的。本篇

将解析kobject,kset,ktype所组织的大框架,还有bus,platfrom到device drivers的框架流程图。抽象到不具体任何具体

设备。加油!之前自己开的坑也会填完,解析linux源码,学习内核需要一定的方法和时间,切勿着急出结果。本文的代

码来自最新的4.11内核。


       

Kobject

        在这之前,必须提到笛卡尔的《方法论》,Kobject的提出背景,及Kobject的重要性,甚至于说是以C语言实现OOB
的方法。
        
struct kobject {const char*name;struct list_headentry;struct kobject*parent;   struct kset*kset;struct kobj_type*ktype;struct kernfs_node*sd; /* sysfs directory entry */struct krefkref;#ifdef CONFIG_DEBUG_KOBJECT_RELEASEstruct delayed_workrelease;#endifunsigned 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;};

        Method of kobject:

           我看了很多的文章,喜欢一开始就总体详细介绍,我总觉得overview能是对作者来说一个总结,而并非读者所希望的
如果一篇文章有一定的逻辑性,循序渐进,才能体现写作者的思路。也是被大家所接受的,kobject,最为一个对象,有它的
method,刚开始关注method,也体现了学习一样东西,先会使用它的思想。
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;//这里我们可以逻辑上认为ktype是与kobject平级的层次 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;}
static inline void kref_init(struct kref *kref){atomic_set(&kref->refcount, 1);}
          init的所有细节,其实比较简单,初始化了一些相关成员。其中与层次有关系的
kobj->ktype = ktype;INIT_LIST_HEAD(&kobj->entry);//其他的以后再讨论,再看第二函数
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;}
static __printf(3, 0) 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);}
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;}parent = kobject_get(kobj->parent);//增加父kobj的引用计数,如何理解这个,请上升到设计的角度分析/* 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,不会理解的,但是可以提前说下,kset是kobject的合集,不指定参数的情况下默认为        //kset的内嵌kobject,之后,kobject的entry加入kset的list成员,注意是双向循环列表的尾部添加一环。error = create_dir(kobj);//创建目录具体的细节比较复杂if (error) {kobj_kset_leave(kobj);kobject_put(parent);kobj->parent = NULL;/* be noisy on error issues */if (error == -EEXIST)WARN(1, "%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));elseWARN(1, "%s failed for %s (error: %d parent: %s)\n",     __func__, kobject_name(kobj), error,     parent ? kobject_name(parent) : "'none'");} elsekobj->state_in_sysfs = 1;return error;}
        该函数形成了框架,总之先kobject_init,接着kobject_add,此时该object会按照一定的规则加入到层次图里面,而sysfs会
有相应的体现,我忽略了create_dir的具体细节,这点做个标记,(默认还忽略了error的分析,对于理解层次框架,error不属于
考虑的范畴)   
        对了一个必须注意的设计细节就是spin_lock在kref上下文的使用,这点我相信值得品味,如果不想写出来到处都是Bug,多
线程一团糟的代码的话。   
/** * kobject_rename - change the name of an object * @kobj: object in question. * @new_name: object's new name * * It is the responsibility of the caller to provide mutual * exclusion between two different calls of kobject_rename * on the same kobject and to ensure that new_name is valid and * won't conflict with other kobjects. */int kobject_rename(struct kobject *kobj, const char *new_name)
        这个函数没有展开细节,相反注释里面,如果调用这个函数,必须提供mutex机制,这个有caller自己完成。值得注意
        kobject_set_name在最新的内核里面被抛弃了。
    const char *kobject_name(const struct kobject * kobj);//现在使用这个函数    int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);//上面单独两个函数的合集,值得注意的是传参数,ktype和kobj的关系,我一直认为是平等的。
        uevent要牵扯很多,如果详细去分析,恐怕要分析到udev,netlink等很多东西
/** * kobject_uevent_env - send an uevent with environmental data * * @kobj: struct kobject that the action is happening to * @action: action that is happening * @envp_ext: pointer to environmental data * * Returns 0 if kobject_uevent_env() is completed with success or the * corresponding error when it fails. */int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,       char *envp_ext[])
        需要另开记录学习帖子
    struct kobject *kobject_get(struct kobject *kobj);    void kobject_put(struct kobject *kobj);
        首先看
 
struct kobject *kobject_get(struct kobject *kobj){if (kobj) {if (!kobj->state_initialized)WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "       "initialized, yet kobject_get() is being "       "called.\n", kobject_name(kobj), kobj);kref_get(&kobj->kref);}return kobj;}
 void kobject_put(struct kobject *kobj){if (kobj) {if (!kobj->state_initialized)WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "       "initialized, yet kobject_put() is being "       "called.\n", kobject_name(kobj), kobj);kref_put(&kobj->kref, kobject_release);}}static inline int kref_sub(struct kref *kref, unsigned int count,     void (*release)(struct kref *kref)){WARN_ON(release == NULL);if (atomic_sub_and_test((int) count, &kref->refcount)) {release(kref);return 1;}return 0;}

        这两个函数反而比较简单,最底层为原子操作,如果kref为0,调用release的方法

      for example:sample/kobject-example.c

static int __init example_init(void){int retval;/* * Create a simple kobject with the name of "kobject_example", * located under /sys/kernel/ * * As this is a simple directory, no uevent will be sent to * userspace.  That is why this function should not be used for * any type of dynamic kobjects, where the name and number are * not known ahead of time. */example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);//该函数只是简单创建kobject,并向sysfs注册if (!example_kobj)return -ENOMEM;        //但是并没有发送uevent,所以到这里可以简单想出来,uevent的作用和角色/* Create the files associated with this kobject */retval = sysfs_create_group(example_kobj, &attr_group);if (retval)kobject_put(example_kobj);return retval;}
        理解attrbute,首先看这个文件中的定义
   
/* * This module shows how to create a simple subdirectory in sysfs called * /sys/kernel/kobject-example  In that directory, 3 files are created: * "foo", "baz", and "bar".  If an integer is written to these files, it can be * later read out of it. */static int foo;static int baz;static int bar;/* * The "foo" file where a static variable is read from and written to. */static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,char *buf){return sprintf(buf, "%d\n", foo);}static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr, const char *buf, size_t count){int ret;ret = kstrtoint(buf, 10, &foo);if (ret < 0)return ret;return count;}/* Sysfs attributes cannot be world-writable. */static struct kobj_attribute foo_attribute =__ATTR(foo, 0664, foo_show, foo_store);//一个属性的完成/* * More complex function where we determine which variable is being accessed by * looking at the attribute for the "baz" and "bar" files. */static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,      char *buf){int var;if (strcmp(attr->attr.name, "baz") == 0)var = baz;elsevar = bar;return sprintf(buf, "%d\n", var);}static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,       const char *buf, size_t count){int var, ret;ret = kstrtoint(buf, 10, &var);if (ret < 0)return ret;if (strcmp(attr->attr.name, "baz") == 0)baz = var;elsebar = var;return count;}static struct kobj_attribute baz_attribute =__ATTR(baz, 0664, b_show, b_store);static struct kobj_attribute bar_attribute =__ATTR(bar, 0664, b_show, b_store);//其他属性的完成,在这个文件里面提供了属性的类型,方法。且该属性是内联的(static)

/* * Create a group of attributes so that we can create and destroy them all * at once.创造了数组,仅仅是编程风格注意,此时数组成员为attribute,并非kobject_attribute */static struct attribute *attrs[] = {&foo_attribute.attr,&baz_attribute.attr,&bar_attribute.attr,NULL,/* need to NULL terminate the list of attributes */};

/* * An unnamed attribute group will put all of the attributes directly in * the kobject directory.  If we specify a name, a subdirectory will be * created for the attributes with the directory being the name of the * attribute group.这里如果我们为attrgroup的name成员赋值,将会导致我们上面的属性文件出现在子目录里面,这个目录就是name */static struct attribute_group attr_group = {.attrs = attrs,};
          回到最上面
/* Create the files associated with this kobject */retval = sysfs_create_group(example_kobj, &attr_group);if (retval)kobject_put(example_kobj);return retval;//此时仅仅是集中创建了属性文件,

         分析sysfs_create_group调用的函数
static int internal_create_group(struct kobject *kobj, int update, const struct attribute_group *grp){struct kernfs_node *kn;int error;BUG_ON(!kobj || (!update && !kobj->sd));/* Updates may happen before the object has been instantiated */if (unlikely(update && !kobj->sd))return -EINVAL;if (!grp->attrs && !grp->bin_attrs) {WARN(1, "sysfs: (bin_)attrs not set by subsystem for group: %s/%s\n",kobj->name, grp->name ?: "");return -EINVAL;}if (grp->name) {//这里判断是否给name赋值kn = kernfs_create_dir(kobj->sd, grp->name,       S_IRWXU | S_IRUGO | S_IXUGO, kobj);//这里与上面对应,也就是如果没有名字会if (IS_ERR(kn)) {                                         //创建目录,否则不创建if (PTR_ERR(kn) == -EEXIST)sysfs_warn_dup(kobj->sd, grp->name);return PTR_ERR(kn);}} elsekn = kobj->sd;kernfs_get(kn);error = create_files(kn, kobj, grp, update);//创建属性文件if (error) {if (grp->name)kernfs_remove(kn);}kernfs_put(kn);return error;}//更具体的细节,对于刨根问底的应该需要自己解析
          现在,我门可以做个试验,来做一些验证,将上述文件,编译成模块并且加载   
          
[root@localhost kernel]# lsboot_params  debug   iommu_groups  kexec_crash_loaded  kexec_loaded  notes      rcu_expedited  security  tracing        vmcoreinfoconfig       fscaps  irq           kexec_crash_size    mm            profiling  rcu_normal     slab      uevent_seqnum[root@localhost kernel]# insmod test.ko insmod: ERROR: could not load module test.ko: No such file or directory[root@localhost kernel]# insmod ~/dhuan/test.ko [root@localhost kernel]# lsboot_params  debug   iommu_groups  kexec_crash_loaded  kexec_loaded     mm     profiling      rcu_normal  slab     uevent_seqnumconfig       fscaps  irq           kexec_crash_size    kobject_example  notes  rcu_expedited  security    tracing  vmcoreinfo[root@localhost kernel]# cd kobject_example/[root@localhost kobject_example]# lsbar  baz  foo[root@localhost kobject_example]# cat foo0[root@localhost kobject_example]# 
         关于属性有更多详尽的讨论,LDD3里面有具体的介绍,但是值得注意的是kobj_type的结构体
    struct kobj_type { void (*release)(struct kobject *kobj);    const struct sysfs_ops *sysfs_ops;    struct attribute **default_attrs;    const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj); const void *(*namespace)(struct kobject *kobj); };
         接下来也引入ktype的概念

Ktype         

          
This structure is used to describe a particular type of kobject (or, more correctly, of containing object). Every kobject needs to have an associated kobj_type structure; a pointer to that structure must be specified when you call kobject_init() or kobject_init_and_add().The release field in struct kobj_type is, of course, a pointer to the release() method for this type of kobject. The other two fields (sysfs_ops and default_attrs) control how objects of this type are represented in sysfs; they are beyond the scope of this document.The default_attrs pointer is a list of default attributes that will be automatically created for any kobject that is registered with this ktype.
          摘自内核说明文档的话,ktype应该视为和kobject平级,甚至为kobject的补充,可能有些kobject具有相同的ktype
          LDD3中:   
Every kobject needs to have an associated kobj_type structure. Confusingly,thepointer to this structure can be found in two different places. The kobject structureitself contains a field (called ktype) that can contain this pointer. If,however,thiskobject is a member of a kset,the kobj_type pointer is provided by that kset instead.
          这时看kset
       

Kset 

        
 * struct kset - a set of kobjects of a specific type, belonging to a specific subsystem. * * A kset defines a group of kobjects.  They can be individually * different "types" but overall these kobjects all want to be grouped * together and operated on in the same manner.  ksets are used to * define the attribute callbacks and other common events that happen to * a kobject. * * @list: the list of all kobjects for this kset * @list_lock: a lock for iterating over the kobjects * @kobj: the embedded kobject for this kset (recursion, isn't it fun...) * @uevent_ops: the set of uevent operations for this kset.  These are * called whenever a kobject has something happen to it so that the kset * can add new environment variables, or filter out the uevents if so * desired. */struct kset {struct list_head list; //我在输入子系统解析过这个结构体,是双向循环链表spinlock_t list_lock; //锁子,看描述还是不清楚,后面看应用会明白struct kobject kobj;//一切都是kobject组织的,kset本身也是kobjectconst struct kset_uevent_ops *uevent_ops;//kset这个成员一定程度上说明Kset的特点,kset所“管理”的Kobject相同的操作方式};
       
     内核的说明文档有下面的描述:
    
- A kset is also a subdirectory in sysfs, where the associated kobjects with the kset can show up.  Every kset contains a kobject which can be set up to be the parent of other kobjects; the top-level directories of the sysfs hierarchy are constructed in this way.- Ksets can support the "hotplugging" of kobjects and influence how uevent events are reported to user space.
      
      在说明它们之间的关系前,必须列出来kset的method

    Method of kset:

      
static struct kset *kset_create(const char *name,const 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, "%s", 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应该初始化的地方,和作为kset本身所需要填充的
地方。
          1.分配kset空间
          2.设置kset->kobject.name  (实参)这个名字代表了kset,注意
                     kset->uevent_ops  (实参)
                     kset->kobj.parent (实参)
                     kset->kobj.ktype = &kset_ktype; 这是static 全局变量,属于kset共同的属性,有兴趣可以自己追踪
             kset->kobj.kset = NULL; 注意kset自己被创建时,自己所属的kset被设置为null。
            
void kset_init(struct kset *k){kobject_init_internal(&k->kobj);//最上面解析过INIT_LIST_HEAD(&k->list);spin_lock_init(&k->list_lock);}//简单的继续初始化
          
int kset_register(struct kset *k){int err;if (!k)return -EINVAL;kset_init(k);err = kobject_add_internal(&k->kobj);//上面解析过,黑盒理解为在sysfs创建相应的目录或文件if (err)return err;kobject_uevent(&k->kobj, KOBJ_ADD);//给用户空间上报事件return 0;}
              在kobject_uevent中,想大概说几个地方
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;const struct kset_uevent_ops *uevent_ops;int i = 0;int retval = 0;#ifdef CONFIG_NETstruct uevent_sock *ue_sk;#endif        .........../* search the kset we belong to */top_kobj = kobj;while (!top_kobj->kset && top_kobj->parent)top_kobj = top_kobj->parent;//kobject依赖parent组织成树形结构,但是kset却把某一类kobject归为一个集合,享有共同的uevent_ops和ktype,        //Kset list可以找到所包含的kobject成员,而每一个kobject的kset域指向自己的kset,这样一个层次图就出来了。kset = top_kobj->kset;uevent_ops = kset->uevent_ops;        .................../* 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; }        ..................../* originating subsystem */ //这里注意下,subsystem!if (uevent_ops && uevent_ops->name)subsystem = uevent_ops->name(kset, kobj);elsesubsystem = 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;}        ..........................}

/** * kset_unregister - remove a kset. * @k: kset. */void kset_unregister(struct kset *k){if (!k)return;kobject_del(&k->kobj);kobject_put(&k->kobj);}
        最后这个比较简单,不做解析
        

       for example:sample/kset-example.c

         
static int __init example_init(void){/* * Create a kset with the name of "kset_example", * located under /sys/kernel/ */example_kset = kset_create_and_add("kset_example", NULL, kernel_kobj);//kernel_kobj是父目录if (!example_kset)                                          //对应的是sys/kernel,return -ENOMEM;/* * Create three objects and register them with our kset */foo_obj = create_foo_obj("foo");//该函数"foo"的object->set被设置为example_ksetif (!foo_obj)goto foo_error;bar_obj = create_foo_obj("bar");if (!bar_obj)goto bar_error;baz_obj = create_foo_obj("baz");if (!baz_obj)goto baz_error;return 0;baz_error:destroy_foo_obj(bar_obj);bar_error:destroy_foo_obj(foo_obj);foo_error:kset_unregister(example_kset);return -EINVAL;}


static struct foo_obj *create_foo_obj(const char *name){struct foo_obj *foo;int retval;/* allocate the memory for the whole object */foo = kzalloc(sizeof(*foo), GFP_KERNEL);if (!foo)return NULL;/* * As we have a kset for this kobject, we need to set it before calling * the kobject core. */foo->kobj.kset = example_kset;//这才是关键,example_kset为static全局变量/* * Initialize and add the kobject to the kernel.  All the default files * will be created here.  As we have already specified a kset for this * kobject, we don't have to set a parent for the kobject, the kobject * will be placed beneath that kset automatically. */retval = kobject_init_and_add(&foo->kobj, &foo_ktype, NULL, "%s", name);if (retval) {kobject_put(&foo->kobj);return NULL;}//注释详细到不用再做任何说明,但是还是抽出来一些细节来品味/* * We are always responsible for sending the uevent that the kobject * was added to the system. */kobject_uevent(&foo->kobj, KOBJ_ADD);//这个具体的作用需要后面解析更多的东西,包括udevreturn foo;}
 
                
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);//其实就是变参版本的kobject_addva_end(args);return retval;}

static __printf(3, 0) 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);//变参版本的kobject_set_nameif (retval) {printk(KERN_ERR "kobject: can not set name properly!\n");return retval;}kobj->parent = parent;return kobject_add_internal(kobj);}
          总体上和以前的函数一样,只不过做了变参的处理,注意foo的创建,kobject->parent为null,而其kset被设置为
example_kset。
          其他的不用再解析了,编译该文件,进行观察:
[root@localhost kernel]# lsboot_params  debug   iommu_groups  kexec_crash_loaded  kexec_loaded  mm     profiling      rcu_normal  slab     uevent_seqnumconfig       fscaps  irq           kexec_crash_size    kset_example  notes  rcu_expedited  security    tracing  vmcoreinfo[root@localhost kernel]# cd kset_example/[root@localhost kset_example]# lsbar  baz  foo[root@localhost kset_example]# cd baz[root@localhost baz]# lsbar  baz  foo[root@localhost baz]# cd ..&& cd foo && lsbar  baz  foo[root@localhost foo]# 
        注意一点,kobject->type三者都设置了相同的值,所以这里呈现了每个目录里面都是三个属性文件bar,baz,foo和代码分析
的也是一致的,
        而kset_example所给其list所指向的kobject提供了uevent_ops的方法。
        到这里已经有点清楚了。
        第一篇暂时到这里,第二篇会解析一些实际代码来体现整个目录的形成过程。