【加入自己的部分内容】Linux设备驱动模型学习之基础篇--Kobject.txt翻译

参考的原文地址:

http://blog.csdn.net/king_208/article/details/5273689

 

------------------------------------------------------------------------------------------

本帖翻译了Linux2.6.29的Kobject.txt文档,由于英文水平不好,所以很多地方都翻译不好,之所以还要翻译,就是想逼着自己看,有些东西必须要多看才能有更深的理解。

对于Linux设备驱动模型的学习,打算先强制自己翻译一些基本的内核文档,然后慢慢学习代码。后面也会陆续贴出源码的分析。希望自己能坚持下去,不放弃,呵呵。

欢迎大家批评指正。 

-------------------------------------------------------------------------------------------

       驱动模型和建立在kobject之上的抽象之所以难以理解,部分原因在于没有一个明显的入口点。处理kobjects需要理解一些不同的相互之间互相引用的类型。为了使事情变得简单,我们将采用“多遍”的方法,从模糊的概念开始并且逐步添加细节。为了这个目的,在这里给出一些我们将要使用到的一些概念。

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

struct kobject {
    const char      *name;
    struct list_head    entry;
    struct kobject      *parent;
    struct 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;
};


 

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

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

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

 

 

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

 

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);
};


 

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

 

 

l         一个kset是一组kobjects

 

/**
 * 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;
    const struct kset_uevent_ops *uevent_ops;
};


 

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

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

 

 

一、嵌入kobjects

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

 

struct uio_mem

{ 

	     struct kobject kobj;

              unsigned long addr;

              unsigned long size;

              int memtype;

              void __iomem *internal_addr; 

};

 

 

struct uio_mem

{ 

              struct kobject kobj;

              unsigned long addr;

              unsigned long size;

              int memtype;

              void __iomem *internal_addr; 

};




 

如果你有一个uio_mem结构体,使用其kobj成员就可以找出嵌入的koject。使用kobjects的代码经常会遇到一个问题:给定一个kobject指针,怎样找出包含该kobject的结构的指针?你必须避免一些“诡计”,比如,假设kobject是某个结构的第一个成员(结构的第一个成员的指针就是该结构的指针),相反,你应该使用container_of宏(<linux/kernel.h>):

       container_of(pointer, type, member)

 

pointer就是嵌入的kobject的指针,type是包含kobject的结构的类型,member是pointer指向的结构里的域的名字。container_of的返回值就是给定type的指针。举个例子,kp指向uio_mem结构里的kobject,那么可以通过如下方式获取指向uio_mem的指针:

    struct uio_mem *u_mem = container_of(kp, struct uio_mem, kobj);

 

程序员通常定义一个简单宏来将kobject指针“后向转换”为其“容器”的类型(即包含kobject的结构的指针)。

 

 

二、初始化kobjects

创建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被关联到一个特殊的kset,在调用kobject_add()之前kobj->kset必须被赋值如果一个kset被关联到一个kobject,那么在kobject_add()调用中该kobject的父节点可被设置为NULL(就是kobject_add()的第二个参数设置为NULL),并且,该kobject的父节点就是那个kset本身

因为kobject的名称在kobject加入内核的时候就被设定了,所以永远不要直接操作一个kobject的名字。如果你必须改变kobject的名字,那么请调用kobject_rename():

    int kobject_rename(struct kobject *kobj, const char *new_name);

 

该函数不会进行任何locking,也不会去检查名字的合法性,所以调用者必须提供locking机制和检查名字的合法性。

还有一个叫做kobject_set_name()的函数,该函数将被删除,所以不要调用这个函数(在将来的l某个inux版本中将不再使用?)。

 

应该使用函数kobject_name()来获取kobject的名字:

    const char *kobject_name(const struct kobject * kobj);

 

还有一个函数用于同时初始化和将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()所描述的参数一样。

 

 

三、热插拔

 

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

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

 

当kobject第一次被加入内核时,使用KOBJ_ADD事件(即 参数 action=KOBJ_ADD。使用该事件时,所有的kobject的属性或孩子(子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的时候,这些默认属性会被自动的添加。

 

七、ksets

一个kset仅仅是那些希望互相关联的kobjects的集合。没有特别的限制非要这些kobjects属于同一ktype,但是如果不是,那么要非常小心

一个kset提供以下功能:

l         它为一组kobjects提供一个容器

一个kset可以被内核用来跟踪所有的块设备或者所有的PCI设备驱动。

 

l         一个kset也是sysfs的一个子目录,在那里与之关联的kobjects会被显示

每个kset都包含一个kobject,该kobject可以被设置为其他kobjects的父节点;sysfs的顶层目录就是通过这种方式构建的

 

l         ksets支持kobjects的热插拔,并且影响着如何向用户空间报告uevent事件

在面向对象领域,kset是顶层的容器类;ksets包含它们自己的kobject,但是那个kobject被kset代码所管理,并且不应该被其他用户操作。

一个kset使用标准的内核链表管理它的孩子节点。Kobjects通过它们的kset成员指向包含它们的kset。基本上所有的情况下,属于某个kset的kobject都会把包含它的kset当作自己的父节点(或者,严格地说,把内嵌于kset的kobject当作父节点)。

由于一个kset内含一个kobject,因此kset总是应该动态地创建而不是静态地声明或者运行在堆栈上。

使用如下代码创建一个kset:

struct kset *kset_create_and_add(const char *name, struct kset_uevent_ops *u,

                               struct kobject *parent);

当结束对kset的使用时,调用void kset_unregister(struct kset *kset);来销毁它。如果一个kset希望控制与之关联的kobjects的uevent操作,可以使用struct kset_uevent_ops:

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, struct kobj_uevent_env *env); };

对于特定的kobject,filter函数允许kset阻止uevent事件被传送到用户空间。如果filter函数返回0,uevent事件将不会被发送。

name函数的调用可以重写发送uevent到用户空间的kset的缺省名字。缺省情况下,该名字就是kset自己的名字,但是如果存在name函数,那么就可以重写缺省的名字。

The uevent function will be called when the uevent is about to be sent to userspace to allow more environment variables to be added to the uevent.

当uevent事件即将被发送至用户空间以允许更多的环境变量加入到该uevent事件时,uevent函数将会被调用(不通-_-….)。

One might ask how, exactly, a kobject is added to a kset, given that no functions which perform that function have been presented. 

(上面那句不会翻译…)

答案是这个工作由kobject_add()来处理。当一个kobject被传递到kobject_add()时,kobject的kset成员应该指向它(kobject)所属的kset,然后kobject_add()处理剩下的事情。

如果属于一个kset的kobject没有父kobject集合(不懂…),它将被加入到kset的目录。并非所有的kset的成员都需要存在于kset的目录。如果在kobject被加入之前,显示地将一个父kobject赋值给该kobject,那么这个kobject会注册进kset,但是被加入到其父kobject目录下。

 

 

八、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。

 

你可能感兴趣的:(数据结构,linux,struct,object,filter,locking)