DSP

uclinux内核的对象表示:kobjects

2019-07-13 17:01发布

快乐虾 http://blog.csdn.net/lights_joy/ lights@hb165.com    本文适用于 ADI bf561 DSP 优视科技BV561EVB开发板 uclinux-2008r1.5-rc3 (smp patch) Visual DSP++ 5.0(update 5)    欢迎转载,但请保留作者信息   内核中使用了kobject来表示与管理一些内核对象,从documentation/kobject的描述可知,kobject兼有以下几个作用: - Object reference counting. - Maintaining lists (sets) of objects. - Object set locking. - Userspace representation. kobjectsysfs文件系统有着密切的关系,所有注册到kobject核心的对象都将在sysfs文件系统中拥有一个目录,从而达到导出内核对象的目的。

1.1    相关数据结构

1.1.1   kref

这个结构体的定义位于include/linux/kref.h,用于表示一个kobject对象的引用计数。 struct kref {      atomic_t refcount; }; 就是一个atomic_t类型的数据。 通常使用kref_init函数对它进行初始化: /**  * kref_init - initialize object.  * @kref: object in question.  */ void kref_init(struct kref *kref) {      atomic_set(&kref->refcount,1);      smp_mb(); } 即将refcount的初值设置为1

1.1.2   kobject

这个结构体用于表示一个内核对象,其定义在include/linux/kobject.h #define KOBJ_NAME_LEN            20   struct kobject {      const char         * k_name;      char          name[KOBJ_NAME_LEN];      struct kref        kref;      struct list_head   entry;      struct kobject         * parent;      struct kset        * kset;      struct kobj_type   * ktype;      struct dentry      * dentry;      wait_queue_head_t  poll; }; l         entry 每个kset下面都可有一长串的kobject列表,此成员即用于此目的。 l         dentry 由于每个kobject都将在sysfs文件系统中创建一个目录,这个值就是指向它的目录节点,因为sysfs是在内存中实现的,因而需要用一个dentry来表示,而不是一个inode l         k_name 这个成员可以单独指定一个名称,如果没有单独指定,它将指向此结构体的name成员。而且k_name不允许为空字符串。   kobject结构体使用kobject_init函数进行初始化: /**  *   kobject_init - initialize object.  *   @kobj:   object in question.  */ void kobject_init(struct kobject * kobj) {      if (!kobj)          return;      kref_init(&kobj->kref);      INIT_LIST_HEAD(&kobj->entry);      init_waitqueue_head(&kobj->poll);      kobj->kset = kset_get(kobj->kset); } 通过此函数,引用计数设置为1 当要引用一个kobject对象时,通常使用kobject_get函数: /**  *   kobject_get - increment refcount for object.  *   @kobj:   object.  */   struct kobject * kobject_get(struct kobject * kobj) {      if (kobj)          kref_get(&kobj->kref);      return kobj; } /**  * kref_get - increment refcount for object.  * @kref: object.  */ void kref_get(struct kref *kref) {      WARN_ON(!atomic_read(&kref->refcount));      atomic_inc(&kref->refcount);      smp_mb__after_atomic_inc(); } kobject_get函数将此kobject对象的引用加1后返回此对象。  

1.1.3   kset

这个结构体用于表示某一类对象(用kobject表示)的集合,其定义为: /**  *   kset - a set of kobjects of a specific type, belonging  *   to a specific subsystem.  *  *   All kobjects of a kset should be embedded in an identical  *   type. This type may have a descriptor, which the kset points  *   to. This allows there to exist sets of objects of the same  *   type in different subsystems.  *  *   A subsystem does not have to be a list of only one type  *   of object; multiple ksets can belong to one subsystem. All  *   ksets of a subsystem share the subsystem's lock.  *  *   Each kset can support specific event variables; it can  *   supress the event generation or add subsystem specific  *   variables carried with the event.  */ 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, char **envp,               int num_envp, char *buffer, int buffer_size); }; struct kset {      struct kobj_type   * ktype;      struct list_head   list;      spinlock_t         list_lock;      struct kobject         kobj;      struct kset_uevent_ops * uevent_ops; }; list这个成员可以很容易知道每个kset下面都将有许多的kobject。从kobj这个成员也可以猜测出内核是将kset也当成一个对象来看待的,呵呵。 通常使用kset_init函数进行此结构体的初始化: /**  *   kset_init - initialize a kset for use  *   @k:  kset  */   void kset_init(struct kset * k) {      kobject_init(&k->kobj);      INIT_LIST_HEAD(&k->list);      spin_lock_init(&k->list_lock); } 注意在此并不改变uevent_ops成员的值,此值通常在定义kset的全局变量时初始化(可以初始化为NULL)。Ktype的值同样在全局变量定义时进行初始化,也可以设置为NULL  

1.1.4   kobj_type

这个类型用以表示对某一类kobject的操作: struct kobj_type {      void (*release)(struct kobject *);      struct sysfs_ops   * sysfs_ops;      struct attribute   ** default_attrs; };  

1.2    对象集的定义:decl_subsys

内核中使用了decl_subsys这个宏来定义一类对象集: #define decl_subsys(_name,_type,_uevent_ops) / struct kset _name##_subsys = { /      .kobj = { .name = __stringify(_name) }, /      .ktype = _type, /      .uevent_ops =_uevent_ops, / } 比如在fs/namespace.c中就定义了这么一个对象集: decl_subsys(fs, NULL, NULL); 这个对象集在mnt_init函数中进行注册:      err = subsystem_register(&fs_subsys); 在内核中搜索,可以发现有不少的decl_subsys   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/block/genhd.c(611):decl_subsys(block, &ktype_block, &block_uevent_ops);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/drivers/base/bus.c(147):static decl_subsys(bus, &ktype_bus, NULL);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/drivers/base/class.c(75):static decl_subsys(class, &ktype_class, NULL);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/drivers/base/class.c(481):static decl_subsys(class_obj, &ktype_class_device, &class_uevent_ops);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/drivers/base/core.c(431):decl_subsys(devices, &ktype_device, &device_uevent_ops);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/drivers/base/firmware.c(19):static decl_subsys(firmware, NULL, NULL);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/drivers/base/sys.c(135):static decl_subsys(system, &ktype_sysdev_class, NULL);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/fs/namespace.c(44):decl_subsys(fs, NULL, NULL);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/kernel/ksysfs.c(66):decl_subsys(kernel, NULL, NULL);   F:/embed/uClinux/uClinux-dist-2008R1.5-RC3/linux-2.6.x/kernel/params.c(697):decl_subsys(module, &module_ktype, &module_uevent_ops); 这些kset的共同特点是它们都没有parent

1.3    注册对象集:subsystem_register

此函数用于注册一个kset,其实现在fs/kobject.c中: int subsystem_register(struct kset *s) {      return kset_register(s); } 跟踪kset_register /**  *   kset_register - initialize and add a kset.  *   @k:  kset.  */   int kset_register(struct kset * k) {      if (!k)          return -EINVAL;      kset_init(k);      return kset_add(k); } 挺简单的,先初始化kset结构体,再将它加入到内核中kset的链表中。 跟踪kset_add /**  *   kset_add - add a kset object to the hierarchy.  *   @k:  kset.  */   int kset_add(struct kset * k) {      return kobject_add(&k->kobj); } 从这里可以明显看出kset本身的确也是一个kobject 跟踪kobject_add /**  *   kobject_add - add an object to the hierarchy.  *   @kobj:   object.  */ int kobject_add(struct kobject * kobj) {      return kobject_shadow_add(kobj, NULL); } 跟踪kobject_shadow_add /**  *   kobject_shadow_add - add an object to the hierarchy.  *   @kobj:   object.  *   @shadow_parent: sysfs directory to add to.  */   int kobject_shadow_add(struct kobject * kobj, struct dentry *shadow_parent) {      int error = 0;      struct kobject * parent;        if (!(kobj = kobject_get(kobj)))          return -ENOENT;      if (!kobj->k_name)          kobj->k_name = kobj->name;      if (!*kobj->k_name) {          pr_debug("kobject attempted to be registered with no name!/n");          WARN_ON(1);          kobject_put(kobj);          return -EINVAL;      }      parent = kobject_get(kobj->parent);        pr_debug("kobject %s: registering. parent: %s, set: %s/n",           kobject_name(kobj), parent ? kobject_name(parent) : "",           kobj->kset ? kobj->kset->kobj.name : "" );        if (kobj->kset) {          spin_lock(&kobj->kset->list_lock);            if (!parent)               parent = kobject_get(&kobj->kset->kobj);            list_add_tail(&kobj->entry,&kobj->kset->list);          spin_unlock(&kobj->kset->list_lock);          kobj->parent = parent;      }        error = create_dir(kobj, shadow_parent);      if (error) {          /* unlink does the kobject_put() for us */          unlink(kobj);          kobject_put(parent);            /* be noisy on error issues */          if (error == -EEXIST)               printk(KERN_ERR "kobject_add failed for %s with "                      "-EEXIST, don't try to register things with "                      "the same name in the same directory./n",                      kobject_name(kobj));          else               printk(KERN_ERR "kobject_add failed for %s (%d)/n",                      kobject_name(kobj), error);          dump_stack();      }        return error; } 此时shadow_parent参数为NULL 在此函数中,如果kobject有指定的kset,则将此object添加到kset的对象链表中,如果没有显式指定parent,则取kset做为它的parent 然后调用create_dir函数在sysfs文件系统中创建此kobject的入口。

1.4    kobject创建目录:create_dir

此函数的定义在lib/kobject.c static int create_dir(struct kobject * kobj, struct dentry *shadow_parent) {      int error = 0;      if (kobject_name(kobj)) {          error = sysfs_create_dir(kobj, shadow_parent);          if (!error) {               if ((error = populate_dir(kobj)))                    sysfs_remove_dir(kobj);          }      }      return error; } 两步走,先用sysfs_create_dir创建目录,如果没有发生错误,它将正确设置好kobject::dentry,使其指向正确的目录指针。如果此kobject有指定的parent,那么sysfs_create_dir将把它挂在其parent目录下,如果没有则挂在sysfs_mount->mnt_sb->s_root下面。 接着用populate_dir设置目录的属性。

1.5    设置kobject属性:populate_dir

此函数的定义在lib/kobject.c中: /**  *   populate_dir - populate directory with attributes.  *   @kobj:   object we're working on.  *  *   Most subsystems have a set of default attributes that  *   are associated with an object that registers with them.  *   This is a helper called during object registration that  *   loops through the default attributes of the subsystem  *   and creates attributes files for them in sysfs.  *  */   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) {