九鼎创展论坛中文版English
登录 | 立即注册 设为首页收藏本站 切换到宽版
查看: 4866|回复: 2
打印 上一主题 下一主题

x4412&ibox项目实战25-kobject简介

[复制链接]
跳转到指定楼层
楼主
发表于 2014-9-28 19:21:17 | 只看该作者 回帖奖励 |倒序浏览 |阅读模式
Kobject是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject数据结构进行描述。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结构。但是,它比较低调,从不单独使用,都是嵌套在别的数据结构中。类似于C++中的基类,它被嵌入更大的对象中,可以称之为所谓的容器,用来描述设备模型的组件。如bus, devices, drivers都是典型的容器。这些容器就是通过kobject连接起来了,形成了一个树状结构,这个树状结构与/sys相对应。
      驱动模型和建立在kobject之上的抽象之所以难以理解,部分原因在于没有一个明显的入口点。处理kobjects需要理解一些不同的相互之间互相引用的类型。为了使事情变得简单,我们将采用“多遍”的方法,从模糊的概念开始并且逐步添加细节。为了这个目的,在这里给出我们将要使用到的一些概念。
一个kboject是类型为struct kobject的一个对象。Kobject有一个名字(name)和一个引用计数(reference count)。一个kobject还包含一个父指针(该指针可以使得对象之间可以分层次排列)、一个特殊的类型(即ktype)、一个在sysfs虚拟文件系统里的表示(representation)。Kobjects基本上并不关注本身,它们通常被嵌入到其他数据结构中,这些数据结构中包含真正受关注的成员。
       一个ktype是包含kobject的对象的类型,每一个包含kobject的结构需要一个对应的ktype当创建和销毁kobject的时候,ktype控制将会发生什么。
一个kset是一组kobjects。这组kobjects可以属于同一ktype,也可以属于不同的ktypes。kset是收集kobjects的基本的容器类型。Ksets也包含它们自己的kobjects,不过可以很安全地忽视那些实现细节,因为kset的核心代码会自动地处理它们自己的kobject。
下面将会介绍如何创建和操作所有这些类型。我们采用自底向上的方法,先回到kobjects的学习。
1.1.1    嵌入kobjects
   内核代码基本上不会创建单独的kobject,但是也有例外。Kobjects被用来控制访问一个更大的、针对特定域的对象。所以,你会发现kobjects常嵌入至其他数据结构中。如果你习惯用面向对象的方式考虑问题,可以认为kobjects是被继承的顶层的抽象基类。Kobject实现了一组操作,这组操作对自身并没有多大的用处,但是对其他对象(包含kobject的对象)来说很有用。C语言并不支持继承关系的直接支持,所以必须使用其他技术比如嵌入数据结构。举个例子,UIO的代码里有一个数据结构定义了关联到一个uio设备的内存边界:
  1.          struct uio_mem
  2.          {
  3.                    struct kobject kobj;
  4.                    unsigned long addr;
  5.                    unsigned long size;
  6.                    int memtype;
  7.                    void __iomem *internal_addr;
  8.          };
复制代码
如果你有一个uio_mem结构体,使用其kobj成员就可以找出嵌入的koject。使用kobjects的代码经常会遇到一个问题:给定一个kobject指针,怎样找出包含该kobject的结构的指针?你必须避免一些“诡计”,比如,假设kobject是某个结构的第一个成员(结构的第一个成员的指针就是该结构的指针),相反,你应该使用container_of宏(<linux/kernel.h>):
  1.        container_of(pointer, type, member)
复制代码
pointer就是嵌入的kobject的指针,type是包含kobject的结构的类型,member是pointer指向的结构里的域的名字。container_of的返回值就是给定type的指针。举个例子,kp指向uio_mem结构里的kobject,那么可以通过如下方式获取指向uio_mem的指针:
  1.     structuio_mem *u_mem = container_of(kp, struct uio_mem, kobj);
复制代码
程序员通常定义一个简单宏来将kobject指针“后向转换”为其“容器”的类型(即包含kobject的结构的指针)。
1.1.2    初始化kobjects
创建kobject的代码当然必须得初始化那个对象。一些内部的域强制使用kobject_init()来初始化:
  1.     voidkobject_init(struct kobject *kobj, struct kobj_type *ktype);
复制代码
因为每个kobject必须和一个kobj_type相关联,所以要想正确地创建kobject就需要一个ktype。调用kobject_init()后,为了在sysfs中注册kobject,函数kobject_add()必须被调用:
  1.     intkobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt,...);
复制代码
该函数可以正确地设置kobject的名称和它的父节点。如果kobject被关联到一个特殊的kset,在调用kobject_add()之前kobj->kset必须被赋值。如果一个kset被关联到一个kobject,那么在kobject_add()调用中该kobject的父节点可被设置为NULL(就是kobject_add()的第二个参数设置为NULL),并且,该kobject的父节点就是那个kset本身。
因为kobject的名称在kobject加入内核的时候就被设定了,所以永远不要直接操作一个kobject的名字。如果你必须改变kobject的名字,那么请调用kobject_rename():
  1.     intkobject_rename(struct kobject *kobj, const char *new_name);
复制代码
该函数不会进行任何locking,也不会去检查名字的合法性,所以调用者必须提供locking机制和检查名字的合法性。
kobject_set_name()函数将被删除,所以不要调用这个函数。应该使用函数kobject_name()来获取kobject的名字:
  1.     constchar *kobject_name(const struct kobject * kobj);
复制代码
kobject_init_and_add()函数用于同时初始化和将kobject加入内核:
  1.     intkobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, structkobject *parent, const char *fmt, ...);
复制代码
该函数的参数和单独的函数kobject_init()和kobject_add()所描述的参数一样。
1.1.3    热插拔
当一个kobject被注册到kobject核心后,需要对外声明该kobject已经被创建。可以通过调用kobject_uevent()来实现:
  1.     intkobject_uevent(struct kobject *kobj, enum kobject_action action);
复制代码
当kobject第一次被加入内核时,使用KOBJ_ADD事件。使用该事件时,所有的kobject的属性都必须已被正确地初始化,因为当KOBJ_ADD发生时用户空间会立刻开始检查它们。
当kobject被从内核移除时,kobject核心会自动创建KOBJ_REMOVE事件,调用者无需手动创建。
1.1.4    引用计数
Kobject的一个关键的功能就是作为包含它的对象的引用计数。只要对这个对象的引用还存在,该对象(和支持该对象的代码)就必须存在。底层操作kobject引用计数的函数是:
  1.     struct kobject *kobject_get(struct kobject *kobj);
  2.     void kobject_put(struct kobject *kobj);
复制代码
正确调用kobject_get()将会增加kobject的引用计数并且返回指向kobject的指针。
当释放一个引用时,调用kobject_put()会减少引用计数,并且有可能会释放对象(当引用计数为0时)。注意,kobject_init()设置引用计数为1,所以设置kobject的代码最终需要调用kobject_put()来释放那个引用。
因为kobjects是动态的,所以它们不能被声明为静态的或者存放在堆栈上,而总是要动态地分配。未来版本的内核会包含对kobject的运行时检查,如果发现kobject是静态创建的,将会警告开发者。
如果仅仅想用kobject作为结构体的引用计数器,那么请使用结构kref;使用kobject太浪费了。想了解kref的信息请参考Documentation/kref.txt。
1.1.5    创建简单的kobjects
有时开发者仅仅希望有一种途径去在sysfs层次中创建一个简单的目录,而不是必须要和复杂的ksets、show和store方法,还有别的细节搞混。这就是一个需要单独创建一个kobject的例外(前面说过一般不单独创建一个kobject的)。为了创建这样一个入口,可以使用函数:
  1.     struct kobject *kobject_create_and_add(char *name, struct kobject *parent);
复制代码
该函数会在sysfs中指定父kobject的下面创建和放置一个kobject。创建简单的和该kobject相关联的属性时,可以使用:
  1. int sysfs_create_file(struct kobject *kobj, struct attribute *attr);
复制代码
或者:
  1. int sysfs_create_group(struct kobject *kobj, struct attribute_group *grp);
复制代码
这里使用在通过kobject_create_and_add()创建的kobject上的两种类型的属性,可以是kobj_attribute类型的,因此不需要创建自定义的属性。
1.1.6    ktypesrelease方法
一个重要的事情还没有被讨论,那就是当一个kobject的引用计数为0时,将会发生什么?创建kobject的代码一般不知道这种情况什么时候会发生(引用计数变为0);引入sysfs后,即使是可以预期的对象生命周期也会变得复杂,因为内核的其他部分可以引用任何注册于系统内的kobject。
最终的结果就是,一个被kobject保护的结构(结构中包含一个kobject)在其引用计数变为0之前不能被释放。创建kobject的代码并不直接控制引用计数。因此,当对kobjects的最后一个引用消失时,代码必须异步地通知。
一旦通过kobject_add()注册了kobject,永远不要直接使用kfree()去释放它。仅有的安全的方法是使用kobject_put()。这个通知是通过kobject的release()方法来完成的。通常这样的方法有一种格式:
  1. void my_object_release(struct kobject *kobj)
  2. {
  3. struct my_object *mine = container_of(kobj, struct my_object, kobj);
  4.            /* Perform any additional cleanup on this object, then... */
  5.     kfree(mine);
  6. }
复制代码
每个kobject都必须有一个release()方法,并且kobject必须处于一个稳定的状态,直到这个方法被调用。如果没有遇到这样的限制,那么代码就是有瑕疵的。注意,如果忘记提供release()方法,内核会给出警告。不要尝试通过提供一个空的release方法来规避这个警告,如果尝试这么做,那么你会被kobject的维护者无情地嘲笑。
注意,在release方法中kobject的名字是可以获取的,但是在回调中必须不能被改变。否则,kobject核心将会出现内存泄漏。有趣的是,release方法并不存在于kobject内部,而是和ktype关联。所以让我们来介绍kobj_type结构:
  1. struct kobj_type
  2. {
  3. void (*release)(struct kobject *);
  4.            struct sysfs_ops     *sysfs_ops;
  5.            struct attribute       **default_attrs;
  6. };
复制代码
这个结构体被用来描述一个特殊类型的kobject(kobject的容器对象)。每个kobject都需要一个相关联的kobj_type结构。当调用kobject_init()或者kobject_init_and_add()时指向kobj_type结构的指针必须被赋值。
结构体kobj_type的release成员是一个指向对应于该类kobject的release方法。其他两个成员sysfs_ops和default_attrs控制如何在sysfs中表示这个类型的对象。
default_attrs指针是默认属性的列表,当创建注册于这个ktype的kobject的时候,这些默认属性会被自动的添加。
1.1.7    ksets
一个kset仅仅是那些希望互相关联的kobjects的集合。没有特别的限制非要这些kobjects属于同一ktype。一个kset为一组kobjects提供一个容器,一个kset可以被内核用来跟踪所有的块设备或者所有的PCI设备驱动。一个kset也是sysfs的一个子目录,在那里与之关联的kobjects会被显示。
每个kset都包含一个kobject,该kobject可以被设置为其他kobjects的父节点;sysfs的顶层目录就是通过这种方式构建的。
ksets支持kobjects的热插拔,并且影响着如何向用户空间报告uevent事件。在面向对象领域,kset是顶层的容器类;ksets包含它们自己的kobject,并且每个kobject都被对应的kset代码所管理,不应该被其他用户操作。
一个kset使用标准的内核链表管理它的子节点。Kobjects通过它们的kset成员指向包含它们的kset。基本上所有的情况下,属于某个kset的kobject都会把包含它的kset当作自己的父节点。
由于一个kset内含一个kobject,因此kset总是应该动态地创建而不是静态地声明或者运行在堆栈上。使用如下代码创建一个kset:
  1. struct kset*kset_create_and_add(const char *name, struct kset_uevent_ops *u,struct kobject*parent);
复制代码
当结束对kset的使用时,调用如下函数来销毁它:
  1. void kset_unregister(structkset *kset);
复制代码
如果一个kset希望控制与之关联的kobjects的uevent操作,可以使用kset_uevent_ops结构体:
  1. struct kset_uevent_ops
  2. {
  3.     int (*filter)(struct kset *kset, struct kobject *kobj);
  4.         const char *(*name)(struct kset *kset, struct kobject *kobj);
  5.     int (*uevent)(struct kset *kset, struct kobject *kobj, struct kobj_uevent_env *env);
  6. };
复制代码
对于特定的kobject,filter函数允许kset阻止uevent事件被传送到用户空间。如果filter函数返回0,uevent事件将不会被发送。
name函数的调用可以重写发送uevent到用户空间的kset的缺省名字。缺省情况下,该名字就是kset自己的名字,但是如果存在name函数,那么就可以重写缺省的名字。
当uevent事件即将被发送至用户空间以允许更多的环境变量加入到该uevent事件时,uevent函数将会被调用。
如果属于一个kset的kobject没有父kobject集合,它将被加入到kset的目录。并非所有的kset的成员都需要存在于kset的目录。如果在kobject被加入之前,显示地将一个父kobject赋值给该kobject,那么这个kobject会注册进kset,但是被加入到其父kobject目录下。
1.1.8    Kobject的移除
当一个kobject被成功地注册进kobject核心后,在代码结束对它的使用时,必须清除它。你可以调用kobject_put(),调用该函数后,kobject核心将会自动释放分配给该kobject的所有内存。如果一个KOBJ_ADD事件被发送到kobject,那么一个对应的KOBJ_REMOVE 事件也将被发送,并且所有其他的sysfs的空间也将会被处理。
如果需要两个阶段来删除kobject(就是说当需要销毁kobject的时候不允许睡眠),调用kobject_del(),该函数会将kobject从sysfs中移除,这将使得kobject不可见,但是并未被清除,并且对象的引用计数也未改变。在稍后的时间里调用kobject_put()来完成与kobject相关联的内存的释放。
如果循环引用构成,kobject_del()可以被用来放弃对父节点的引用。这在有些场合很有效,比如一个父节点引用一个子节点。必须使用kobject_del()来破坏循环引用,之后一个release方法将被调用,并且之前环路中的对象互相release。

回复

使用道具 举报

沙发
发表于 2014-11-28 18:16:45 | 只看该作者
很好,
回复 支持 反对

使用道具 举报

板凳
发表于 2015-9-16 16:29:15 | 只看该作者
这张概念有点多~~~~~~~~赞!
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

QQ|手机版|小黑屋|深圳市九鼎创展科技官方论坛 ( 粤ICP备11028681号-2  

GMT+8, 2024-11-23 17:24 , Processed in 0.020175 second(s), 17 queries .

Powered by Discuz! X3.2

© 2001-2013 Comsenz Inc.

快速回复 返回顶部 返回列表