/*整理于2012.11.29于广工大 */
先说说sysfs
sysfs文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject对象层次结构的视图。帮助用户可以以一个简单文件系统的方式来观察系统中各种设备的拓扑结构。
借助属性对象,kobject可用导出文件的方式,将内核变量提供给用户读取或写入。
sysfs的诀窍是把kobject对象与目录项紧密联系起来,这点是通过kobject中的dentry(directory entry)字段实现的。
dentry结构体表示目录项,通过连接kobject到指定的目录项上,无疑方便的将kobject映射到该目录上。
kobject其实已经形成了一棵树了——就是我们心爱的对象模型体系。
sysfs的根目录下包含了七个子目录:block、bus、class、devices、firmware、module和power。
block目录下的每个子目录都对应着系统中的一个块设备。反过来,每个目录下又都包含了该块设备的所有分区。
bus目录提供了一个系统总线视图。
class目录包含了以高层功能逻辑组织起来的系统设备视图。
devices目录是系统中设备拓扑结构视图,它直接映射出了内核中设备结构体的组织层次。
firmware目录包含一些诸如ACPI、EDD、EFI等低层子系统的特殊树。
power目录包含了系统范围的电源管理数据。
sysfs组织结构,进入sysfs目录中。有block bus class dev devices firmware fs kernel module power这些目录。
具体代表看名字差不多就可以看出。在层次结构上,假如有一个设备A。将有一个名称为A的目录。A设备是在B总线上。
那A设备应该在bus目录下的B总线下。A设备肯定会有设备的属性(ktype),例如是音频设备则应该有音量属性,
则音量属性将在A设备目录下有个音量属性文件。在使用设备时,如果要改变音量大小,则可以写属性文件入音量指。
得到音量大小时,可以读取属性文件中的音量值。
入正题,kobject.
Kobject是Linux 2.6引入的新的设备管理机制,在内核中由struct kobject表示。通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管理,它与sysfs文件系统紧密关联,每个在内核中注册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结构。
上层结构例如device,device_driver,bus_type都嵌入了一个kobject,这相当于面向对象程序设计机制中的继承机制。
kobject 结构为一些大的数据结构和子系统提供了基本的对象管理,避免了类似机能的重复实现。这些机能包括
- 对象引用计数.
- 维护对象链表(集合).
- 对象上锁.
- 在用户空间的表示.
kobject结构定义:
struct kobject {
const char *name;/*名称*/
struct list_head entry;/*用于链入所属的kset的链表*/
struct kobject *parent;/*父object*/
struct kset *kset;/*所属kset*/
struct kobj_type *ktype;/*所属ktype*/
struct sysfs_dirent *sd;/*sysfs中的目录项*/
struct kref kref;/*生命周期(引用计数)管理*/
unsigned int state_initialized:1;/*标记:初始化*/
unsigned int state_in_sysfs:1;/*标记:在sysfs中*/
unsigned int state_add_uevent_sent:1;/*标记:已发出KOBJ_ADD uevent*/
unsigned int state_remove_uevent_sent:1;/*标记:已发出的KOBJ_REMOVE uevent*/
unsigned int uevent_suppress:1;/*标记:禁止发出uevent*/
};
name指针指向kobject名称的起始位置,如果名称长度小于KOBJ_NAME_LEN,当前为20个字节,那么该kobject的名称便存放到name数组中,name指向数组头;如果名称大于20,则动态分配以讹足够大的缓冲区来存放kobject的名称,这时name指向缓冲区。
parent指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构,并且可以将对各对象间的关系表现出来,就如你看到的,这便是sysfs的真正面目:一个用户空间的文件系统,用来表示内核中kobject对象的层次结构。
dentry指针指向dentry结构体,在sysfs中该结构体就表示这个kobject,当然假设该kobject已反映在sysfs中。
kobject通常是嵌入到其他结构中的,其单独意义其实并不大。相反,那些更为重要的结构体才真正需要用到kobject结构。比如struct cdev。
当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准功能。更重要的一点是,嵌入kobject结构体可以成为对象层次架构中的一部分。比如cdev结构体就可以通过其父进程指针cdev->kobj->parent和链表 cdev->kobj->entry来插入到对象层次结构中。
很重要的一点是:每个kobject都必须有一个release()方法,并且kobject必须持久(在一个稳定的状态),直到这个方法被调用。如果没有遇到这样的限制,那么代码就是有瑕疵的。
注意,如果你忘记提供release()方法,内核会给出警告。不要尝试通过提供一个“空的”release方法来规避这个警告;如果你尝试这么做,那么你会被kobject的维护者无情地嘲笑。
注意,在release方法中kobject的名字是可以获取的,但是在回调中必须不能被改变。否则,kobject核心将会出现内存泄漏。
有趣的是,release方法并不存在于kobject内部,而是和ktype关联。
ktype结构体被用来描述一个特殊类型的kobject(或者,更准确点说,描述kobject的“容器”对象)。
每个kobject都需要一个相关联的kobj_type结构;当你调用kobject_init()或者kobject_init_and_add()时指向kobj_type结构的指针必须被先赋值。
ktype是为了描述一族kobject所具有的普遍特性。因此,不再需要每个kobject都分别定义自己的特性,而是将这些普遍的特性在ktype结构体中一次定义,然后所有“同类”的kobject都能共享一样的特性。
kobject对象被关联到一种特殊的类型,即ktype. ktype由kobj_type结构体表示,定义于<linux/kobject.h>中:
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);
};
release:
release成员是一个指向对应于该类kobject的release方法。
release指针指向在kobject引用计数减至0时要被调用的析构函数。
该函数负责释放所有kobject使用的内存和其它相关清理工作,注意必须得提供release函数。
每个 kobject必须有一个release函数,并且这个 kobject必须在release函数被调用前保持不变(稳定状态 )。
这样,每一个 kobject需要有一个关联的 kobj_type结构,指向这个结构的指针能在 2个不同的地方找到:
(1)kobject结构自身包含一个成员(ktype)指向kobj_type;
(2)如果这个 kobject 是一个 kset的成员, kset会提供kobj_type指针。
sysfs_ops和default_attrs控制如何在sysfs中表示这个类型的对象:
sysfs_ops变量指向sysfs_ops结构体。该结构体描述了sysfs文件读写时的特性。
default_attrs指向一个attribute结构体数组。
default_attrs指针是默认属性的列表,当创建注册这个ktype的kobject的时候,这些默认属性会被自动的添加。
这些结构体定义了该kobject相关的默认属性。属性给定了对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应的作为文件而导出。
再说说kset
kset是kobject对象的集合体。把它看成一个容器,可将所有相关的kobject对象,比如“全部的块设备”置于同一位置。
kset可把 kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性,它们之间的重要区别在于:具有相同ktype的kobject可以分到不同的kset中(同一个kset中的kobject是否具有相同的ktype呢)。kobject的kset指针指向相应的kset集合。
struct kset {
struct list_head list;
spinlock_t list_lock;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};
其中ktype指针指向集合(kset)中的kobject对象的类型(ktype),list连接该集合(kset)中所有的kobject对象。
kobj指向的kobject对象代表了该集合的基类。hotplug_ops指向一个用于处理集合中kobject对象的热拔插操作的结构体。
subsys指针指向该结构体相关的struct subsystem结构体。
subsystem在内核中代表高层概念,它是一个或多个kset的大集合。
struct subsystem {
struct kset kset;
struct rw_semaphore rwsem;
};
虽然subsystem结构体只指向一个kset,但是多个kset可以通过其subsys指针指向一个subsystem这种单向关系意味着不可能仅仅通过一个subsystem结构体就找到所有的ksets。
subsystem中的kset字段指向的是subsystem中的默认kset,它在对象层次结构中起到了粘合剂的作用。
rwsem是一个读写信号量,它用来对subsystem和它的所有kset进行并发访问保护。
下面详述kobject的一些基本操作:
引用计数
kobject的主要功能之一就是为我们提供了一个统一的引用计数系统。初始化后,kobject的引用计数设置为1。
只要引用计数不为0,那么该对象就会继续保留在内存中,也可以说是被“钉”住了,任何包含对象引用的代码首先要增加该对象的引用计数,当代码结束后则减少它的引用计数。当引用计数减为0时,对象便可以被销毁,同时相关内存也都被释放。
增加一个引用计数可通过kobject_get()函数完成:
struct kobject * kobject_get(struct kobject *kobj);
该函数正常情况下返回一个指向kobject的指针,如果失败,则返回NULL指针。
减少引用计数通过kobject_put()完成:
void kobject_put(struct kobject *kobj);
Kobject的添加:
仅仅初始化一个kobject是不能自动将其导出到sysfs中的,想要把kobject导入sysfs,需要用到函数kobject_add():
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...);
kobject在sysfs中的位置取决于kobject在对象层次结构中的位置。如果kobject的父指针被设置,那么在sysfs中kobject将被映射为其父目录下的子目录,如果parent没有设置,那么kobject将被映射到kset->kobj中的子目录。两者都未设置,映射为 sysfs下的根级目录。
如果kobject被关联到一个特殊的kset,在调用kobject_add()之前kobj->kset必须被赋值。
如果一个kset被关联到一个kobject,那么在kobject_add()调用中该kobject的父节点可被设置为NULL(就是kobject_add()的第二个参数设置为NULL),并且,该kobject的父节点就是那个kset本身。
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。
向sysfs中添加文件
我们已经看到kobject被映射为文件目录,而且所有的对象层次结构都优雅的、一个不少的映射成为sys下的目录结构。
sysfs仅仅是一个漂亮的树,但是没有提供实际数据的文件。
默认属性
默认的文件集合是通过kobject和kset中的ktype字段提供的。因此所有具有相同类型的kobject在它们对应的sysfs目录下都拥有相同的默认文件集合。
kobj_type字段含有一个字段--default_attrs,它是指向一个attribute结构体数组。这些属性负责将内核数据映射成sysfs中的文件。
struct attribute {
char * name;
struct module * owner;
mode_t mode;
};
其中name字段提供了该属性的名称,最终出现在sysfs中的文件名就是它。
owner字段在存在所属模块的情况下指向其所属的module结构体。如果一个模块没有该属性,那么该字段为NULL。
mode字段类型为mode_t,他表示sysfs中该文件的权限。
default_attrs列出了默认的属性,sysfs_ops字段则描述了如何使用它们。
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
show()方法在读操作时被调用。它会拷贝由attr提供的属性值到buffer指定的缓冲区,缓冲区大小为PAGE_SIZE字节;该函数若执行成功,返回写入buffer的字节数,失败,返回负的错误码。
store()方法在写操作时调用,它会从buffer中读取size大小的字节,并将其放入attr表示的属性结构体变量中。
总结:
kobject大致使用流程:
1.创建kobject
struct kobject my_kobj;
2.创建此kobject中的ktype(名字任意起,kobj_type类型)
2.1
struct kobj_type ktype = {
.release = obj_release,
.sysfs_ops = &obj_sysops,
.default_attrs = def_attrs, //该文件夹下的文件def_attrs
};
注意:一定要提供release函数,即使此函数中可以什么都不做。
再分别对obj_release()、obj_sysops、def_attrs的实现:
2.2
void obj_release(struct kobject *kobject);
2.3
struct sysfs_ops obj_test_sysops = {
.show = kobj_show,
.store = kobj_store,
};
ssize_t kobj_show(struct kobject *kobject, struct attribute *attr,char *buf)
ssize_t kobj_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count);
2.4
static struct attribute *def_attrs[] = {
&test_attr, //其中的一个文件test_attr
NULL, //若多个文件,可继续同样格式添加
};
//文件属性
struct attribute test_attr = {
.name = "kobj_config",
.mode = S_IRWXUGO,
};
3.初始化和注册kobject
void kobject_init(struct kobject *kobj, struct kobj_type *ktype);
int kobject_add(struct kobject *kobj, struct kobject *parent,const char *fmt, ...);
kobj--需创建的kobject;
parent--若此kobject无父类,即为NULL,此kobject即出现在sysfs目录下;
fmt--所创建的kobject在sysfs目录下显示的名字,用户可见的名字;
或者一步初始化和注册kobject加入内核(推荐)
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...);
4.删除kobject
void kobject_del(struct kobject *kobj);
下面再给出一个使用实例,已经通过red hat测试通过
#include <linux/device.h> #include <linux/module.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/string.h> #include <linux/sysfs.h> #include <linux/stat.h> void obj_test_release(struct kobject *kobject); ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf); ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count); /* 文件属性 */ struct attribute test_attr = { .name = "kobj_config", .mode = S_IRWXUGO, }; /* 文件组 */ static struct attribute *def_attrs[] = { &test_attr, //其中的一个文件test_attr NULL, //若多个文件,可继续添加 }; struct sysfs_ops obj_test_sysops = { .show = kobj_test_show, .store = kobj_test_store, }; /* 初始化ktype,注意:必须的提供release函数 */ struct kobj_type ktype = { .release = obj_test_release, .sysfs_ops = &obj_test_sysops, .default_attrs = def_attrs, //该文件夹下的文件def_attrs }; void obj_test_release(struct kobject *kobject) { printk("eric_test: release .\n"); } ssize_t kobj_test_show(struct kobject *kobject, struct attribute *attr,char *buf) { printk("have show.\n"); printk("attrname:%s.\n", attr->name); sprintf(buf,"%s\n",attr->name); return strlen(attr->name)+2; } ssize_t kobj_test_store(struct kobject *kobject,struct attribute *attr,const char *buf, size_t count) { printk("havestore\n"); printk("write: %s\n",buf); return count; } /* 创建kobject */ struct kobject kobj; static int kobj_test_init() { printk("kboject test init.\n"); /* kobject的初始化和添加 */ kobject_init_and_add(&kobj, &ktype, NULL, "kobject_test"); /* 第三个参数为NULL,即为在sysfs目录下创建此kobject */ return 0; } /** * int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...) * * kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy * @kobj: pointer to the kobject to initialize * @ktype: pointer to the ktype for this kobject. * @parent: pointer to the parent of this kobject. * @fmt: the name of the kobject. * * This function combines the call to kobject_init() and * kobject_add(). The same type of error handling after a call to * kobject_add() and kobject lifetime rules are the same here. */ static int kobj_test_exit() { printk("kobject test exit.\n"); kobject_del(&kobj); return 0; } module_init(kobj_test_init); module_exit(kobj_test_exit); MODULE_LICENSE("GPL");