sysfs之kobject分析

来源:互联网 发布:厦门铠甲网络 陈滨 编辑:程序博客网 时间:2024/06/02 13:33

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

1.一个kboject是类型为structkobject的一个对象

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

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

 永远不要让一个数据结构中包含超过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是一种数据结构,定义在中 。
struct kobject{   
constchar     *name;            
structlist_head   entry;        
structkobject     *parent;      
structkset    *kset;            
structkobj_type   *ktype;       
struct sysfs_dirent*sd;   
structkref    kref;             
unsigned intstate_initialized:1;   
unsigned intstate_in_sysfs:1;   
unsigned intstate_add_uevent_sent:1;   
unsigned intstate_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语言并不支持继承关系的直接支持,所以必须使用其他技术—比如嵌入数据结构。

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

调用kobject_init()后,为了在sysfs中注册kobject,函数kobject_add()必须被调用:
    intkobject_add(struct kobject *kobj, struct kobject *parent, constchar *fmt, ...);
还有一个函数用于同时初始化和将kobject加入内核,即kobject_init_and_add():
    intkobject_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(structkobject * kobj) kobject注册函数。通过调用kobjectinit()初始化kobj,再调用kobject_add()完 成该内核对象的注册。

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

该函数可以正确地设置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名称
    intkobject_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_actionaction);

当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的)。为了创建这样一个入口,可以使用函数:
    structkobject *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(structkobject *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);

          

          kfree(mine); }

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

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

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

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

          structsysfs_ops    *sysfs_ops;

          structattribute      **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 {
    structkobj_type    *ktype;
    structlist_head   list;
   spinlock_t       list_lock;
    structkobject       kobj;
    structkset_uevent_ops   * 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_ADDuevent被发送到kobject,那么一个对应的KOBJ_REMOVEuevent也将被发送,并且所有其他的sysfs的空间管理也将会被处理。

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

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

0 0
原创粉丝点击