现在的位置: 首页 > 综合 > 正文

sysfs之kobject

2013年09月26日 ⁄ 综合 ⁄ 共 8358字 ⁄ 字号 评论关闭

一 ,Kobjects概述
 Kobject 是Linux 2.6 引入的新的设备管理机制,在内核中由struct kobject数据结构 进行描述通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管 理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注 册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结 构。但是,它比较低调,从不单独使用,都是嵌套在别的数据结构中。类似于C++中的基 类,它嵌入于更大的对象的对象中–所谓的容器–用来描述设备模型的组件。如bus,
devices, drivers 都是典型的容器。这些容器就是通过kobject连接起来了,形成了 一个树状结构。这个树状结构就与/sys相对应。
       驱动模型和建立在kobject之上的抽象之所以难以理解,部分原因在于没有一个明显的入口点。处理kobjects需要理解一些不同的相互之间互相引用的类型。为了使事情变得简单,我们将采用“多遍”的方法,从模糊的概念开始并且逐步添加细节。为了这个目的,在这里给出一些我们将要使用到的一些概念。

1, 一个kboject是类型为struct kobject的一个对象

       Kobject有一个名字(name)和一个引用计数(reference count)。一个kobject还包含一个父指针(该指针可以使得对象之间可以分层次排列)、一个特殊的类型(即ktype)、一个在sysfs虚拟文件系统里的表示(representation)。

       Kobjects基本上并不关注本身,它们通常被嵌入到其他数据结构中,这些数据结构中包含真正受关注的成员。

       永远不要让一个数据结构中包含超过1个kobject,如果真的这么做了,那么对该对象的引用计数必定一团糟并且不正确,你的代码也将充满bug,所以千万别这么做。

2, 一个ktype是 包含kobject的对象 的类型

       每一个包含kobject的结构需要一个对应的ktype。当创建和销毁kobject的时候,ktype控制将会发生什么。

3, 一个kset是一组kobjects

       这组kobjects可以属于同一ktype,也可以属于不同的ktypes。Kset是收集kobjects的基本的容器类型。Ksets也包含它们自己的kobjects,不过你可以很安全地忽视那些实现细节,因为kset的核心代码会自动地处理它们自己的kobject。

       我们将会学习如何创建和操作所有这些类型。我们将采用自底向上的方法,先回到kobjects的学习。

4 ,kobject是一种数据结构,定义在<linux/kobject.h>中 。
struct kobject {   
const char      *name;          /* 指向设备名称的指针 */   
struct list_head    entry;      /* 挂接到所在kset中去的单元 */   
struct kobject      *parent;    /* 指向父对象(kobject)的指针 */   
struct kset     *kset;          /* 所属kset的指针 */   
struct kobj_type    *ktype;     /* 指向其对象类型描述符的指针 */   
struct sysfs_dirent *sd;   
struct kref     kref;           /* 对象引用计数 */   
unsigned 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;
};其中,重要的变量已经加了注释,这里再简要介绍一下:

kref:
kref域表示该对象引用的计数,内核通过kref实现对象引用计数管理,内核提供两 个函数kobject_get()、kobject_put() 分别用于增加和减少引用计数,当引用计数为 0时,所有该对象使用的资源释放。

Ktype:
域是一个指向kobj type结构的指针,表示该对象的类型。
parent
指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构, 并且可以将对各对象间的关系表现出来,就如你看到的,这便是sysfs的真正面目: 一个用户空间的文件系统,用来表示内核中kobject对象的层次结构。
kobject 是组成设备模型的基本结构,初始它只被作为一个简单的引用计数, 但随时间的推移,其任务越来越多。现在kobject 所处理的任务和支持代码包括:
对象的引用计数 :跟踪对象生命周期的一种方法是使用引用计数。当没有内核代码持有该对象的引用时, 该对象将结束自己的有效生命期并可被删除。
sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。
热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。
一个kobject对自身并不感兴趣,它存在的意义在于把高级对象连接到设备模型上。因此内核代码很少(甚至不知道)创建一个单独的 kobject;而kobject 被用来控制对大型域(domain)相关对象的访问,所以kobject 被嵌入到其他结构中。kobject 可被看作一个最顶层的基类,其他类都它的派生产物。 kobject 实现了一系列方法,对自身并没有特殊作用,而对其他对象却非常有效。
对于给定的kobject指针,可使用container_of宏得到包含它的结构体的指针。

    内核代码基本上不会创建单独的kobject,但是也有例外(后面解释)。Kobjects被用来控制访问一个更大的、针对特定域的对象。所以,你会发现kobjects常嵌入至其他数据结构中。如果你习惯用面向对象的方式考虑问题,可以认为kobjects是被继承的顶层的抽象基类。Kobject实现了一组操作,这组操作对自身并没有多大的用处,但是对其他对象(包含kobject的对象)来说很有用。C语言并不支持继承关系的直接支持,所以必须使用其他技术—比如嵌入数据结构。

二、操作kobjects
15,初始化和注册kobject
创建kobject的代码当然必须得初始化那个对象。一些内部的域强制使用kobject_init()来初始化:
    void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
因为每个kobject必须和一个kobj_type 相关联,所以要想正确地创建kobject就需要一个ktype。

调用kobject_init()后,为了在sysfs中注册kobject,函数kobject_add()必须被调用:
    int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
还有一个函数用于同时初始化和将kobject加入内核,即kobject_init_and_add():
    int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);
该函数的参数和单独的函数kobject_init()和kobject_add()所描述的参数一样。
int kobject_add(struct kobject * kobj) 将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录 各级kobject的引用计数,在其 parent指向的目录下创建文件节点,并启动该类型内 核对象的hotplug函数。

void kobject_del(struct kobject * kobj) 从Linux设备层次(hierarchy)中删除kobj对象。

int kobject_register(struct kobject * kobj) kobject注册函数。通过调用kobject init()初始化kobj,再调用kobject_add()完 成该内核对象的注册。

void kobject_unregister(struct kobject * kobj) kobject注销函数。与kobject register()相反,它首先调用kobject del从设备层次 中删除该对象,再调

该函数可以正确地设置kobject的名称和它的父节点。如果kobject被关联到一个特殊的kset,在调用kobject_add()之前kobj->kset必须被赋值。如果一个kset被关联到一个kobject,那么在kobject_add()调用中该kobject的父节点可被设置为NULL(就是kobject_add()的第二个参数设置为NULL),并且,该kobject的父节点就是那个kset本身。
2,kobject名称
因为kobject的名称在kobject加入内核的时候就被设定了,所以永远不要直接操作一个kobject的名字。如果你必须改变kobject的名字,那么请调用kobject_rename():设置kobject名称
    int kobject_rename(struct kobject *kobj, const char *new_name);
该函数不会进行任何locking,也不会去检查名字的合法性,所以调用者必须提供locking机制和检查名字的合法性。

还有一个叫做kobject_set_name()的函数,该函数将被删除,所以不要调用这个函数。

应该使用函数kobject_name()来获取kobject的名字:
    const char *kobject_name(const struct kobject * kobj);

三、热插拔
当一个kobject被注册到kobject核心后,需要对外声明该kobject已经被创建。可以通过调用kobject_uevent()来实现:

    int kobject_uevent(struct kobject *kobj, enum kobject_action action);

当kobject第一次被加入内核时,使用KOBJ_ADD事件。使用该事件时,所有的kobject的属性或孩子都必须已被正确地初始化,因为当KOBJ_ADD发生时用户空间会立刻开始检查它们。
当kobject被从内核移除时,kobject核心会自动创建KOBJ_REMOVE事件,调用者无需手动创建。

四、引用计数
Kobject的一个关键的功能就是作为包含它的对象的引用计数。只要对这个对象的引用还存在,该对象(和支持该对象的代码)就必须存在。底层操作kobject引用计数的函数是:

    struct kobject *kobject_get(struct kobject *kobj);

    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。

五、创建“简单”的kobjects
有时开发者仅仅希望有一种途径去在sysfs层次中创建一个简单的目录,而不是必须要和复杂的ksets、show和store方法,还有别的细节搞混。这就是一个需要单独创建一个kobject的例外(前面说过一般不单独创建一个kobject的)。为了创建这样一个入口,可以使用函数:
    struct kobject *kobject_create_and_add(char *name, struct kobject *parent);
该函数会在sysfs中指定父kobject的下面创建和放置一个kobject。

创建简单的和该kobject相关联的属性时,可以使用:
int sysfs_create_file(struct kobject *kobj, struct attribute *attr);

或者int sysfs_create_group(struct kobject *kobj, struct attribute_group *grp);

这里使用在通过kobject_create_and_add()创建的kobject 上的两种类型的属性,可以是kobj_attribute类型的,因此不需要创建自定义的属性。

六、ktypes和release方法
一个重要的事情还没有被讨论,那就是当一个kobject的引用计数为0时,将会发生什么?创建kobject的代码一般不知道这种情况什么时候会发生(引用计数变为0);引入sysfs后,即使是可以预期的对象生命周期也会变得复杂,因为内核的其他部分可以引用任何注册于系统内的kobject。

最终的结果就是,一个被kobject保护的结构(结构中包含一个kobject)在其引用计数变为0之前不能被释放。创建kobject的代码并不直接控制引用计数。因此,当对kobjects的最后一个引用消失时,代码必须异步地通知。

一旦你通过kobject_add()注册了你的kobject,永远不要直接使用kfree()去释放它。仅有的安全的方法是使用kobject_put()。

这个通知是通过kobject的release()方法来完成的。通常这样的方法有一种格式:

    void my_object_release(struct kobject *kobj)

    { struct my_object *mine = container_of(kobj, struct my_object, kobj);

           /* Perform any additional cleanup on this object, then... */

           kfree(mine); }

很重要的一点是:每个kobject都必须有一个release()方法,并且kobject必须持久(在一个稳定的状态),直到这个方法被调用。如果没有遇到这样的限制,那么代码就是有瑕疵的。注意,如果你忘记提供release()方法,内核会给出警告。不要尝试通过提供一个“空的”release方法来规避这个警告;如果你尝试这么做,那么你会被kobject的维护者无情地嘲笑。

注意,在release方法中kobject的名字是可以获取的,但是在回调中必须不能被改变。否则,kobject核心将会出现内存泄漏,令人不快。

有趣的是,release方法并不存在于kobject内部,而是和ktype关联。所以让我们来介绍kobj_type结构:

    struct kobj_type { void (*release)(struct kobject *);

           struct sysfs_ops     *sysfs_ops;

           struct attribute       **default_attrs; };

这个结构体被用来描述一个特殊类型的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的时候,这些默认属性会被自动的添加。

每个 kobject 必须有一个release函数, 并且这个 kobject 必须在release函数被调用前保持不变( 稳定状态 ) 。这样,每一个 kobject 需要有一个关联的 kobj_type 结构,指向这个结构的指针能在 2 个不同的地方找到:
(1)kobject 结构自身包含一个成员(ktype)指向kobj_type ;
(2)如果这个 kobject 是一个 kset 的成员, kset 会提供kobj_type 指针。
struct kset {
    struct kobj_type    * ktype; /*指向该kset对象类型的指针*/
    struct list_head    list;/*用于连接该kset中所有kobject以形成环形链表的链表头*/
    spinlock_t        list_lock;/*用于避免竞态的自旋锁*/
    struct kobject        kobj; /*嵌入的kobject*/
    struct kset_uevent_ops    * uevent_ops;
/*原有的struct kset_hotplug_ops * hotplug_ops;已经不存在,被kset_uevent_ops 结构体替换,在热插拔操作中会介绍*/
};
以下宏用以查找指定kobject的kobj_type 指针:
struct kobj_type *get_ktype(struct kobject *kobj);
这个函数其实就是从以上提到的这两个地方返回kobj_type指针,源码如下:
static inline struct kobj_type * get_ktype(struct kobject * k)
{
    if (k->kset && k->kset->ktype)
        return k->kset->ktype;
    else
        return k->ktype;
}

七、ksets

 

八、Kobject的移除

当一个kobject被成功地注册进kobject核心后,在代码结束对它的使用时,必须清除它。你可以调用kobject_put(),调用该函数后,kobject核心将会自动释放分配给该kobject的所有内存。如果一个KOBJ_ADD uevent被发送到kobject,那么一个对应的KOBJ_REMOVE uevent也将被发送,并且所有其他的sysfs的空间管理也将会被处理。

如果你需要两个阶段来删除kobject(就是说当需要销毁kobject的时候不允许睡眠),调用kobject_del(),该函数会将kobject从sysfs中移除,这将使得kobject不可见,但是并未被清除,并且对象的引用计数也未改变。在稍后的时间里调用kobject_put()来完成与kobject相关联的内存的释放。

如果循环引用构成,kobject_del()可以被用来放弃对父节点的引用。这在有些场合很有效,比如一个父节点引用一个子节点。必须使用kobject_del()来破坏循环引用,之后一个release方法将被调用,并且之前环路中的对象互相release。

 

关于ksets和kobjects更详细的例子,请参考sample/kobject/kset-example.c code。

 

本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/king_208/archive/2010/01/30/5273689.aspx

 

抱歉!评论已关闭.