分类: LINUX
2010-08-19 19:14:56
17.1 kobject
设备模型的核心部分就是kobject,它由struct kobject结构表示,定义于头文件(linux/kobject)中。kobject类似于C#或java这些面向对象语言中的object对象类, 提供了诸如引用计数、名称和父指针等字段,可以创建对象的层次结构。
struct kobject {
char * k_name;
char name[KOBJ_NAME_LEN];
struct kref kref;
struct list_head entry;
struct kobject * parent;
struct kset * kset;
struct kobj_type * ktype;
struct dentry * dentry;
};
k_name指针指向kobject名称的起始位置,如果名称长度小于KOBJ_NAME_LEN——当前为20个字节,那么该kobject的名称便存
放到name数组中,k_name指向数组头;如果名称大于20,则动态分配以讹足够大的缓冲区来存放kobject的名称,这时k_name指向缓冲
区。
parent指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构,并且可以将对各对象间的关系表现出来,就如你看到 的,这便是sysfs的真正面目:一个用户空间的文件系统,用来表示内核中kobject对象的层次结构。
dentry指针指向dentry结构体,在sysfs中该结构体就表示这个kobject,当然假设该kobject已反映在sysfs中。
kobject通常是嵌入到其他结构中的,其单独意义其实并不大。相反,那些更为重要的结构体才真正需要用到kobject结构。比如struct cdev。
struct cdev {
struct kobject kobj;
struct module *owner;
struct file_operations *ops;
struct list_head list;
dev_t dev
unsigned int count;
};
当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准功能。更重要的一点是,嵌入kobject接结构体可以成为对象层次 架构中的一部分。比如cdev结构体就可以通过其父进程指针cdev->kobj->parent和链表 cdev->kobj->entry来插入到对象层次结构中。
17.2 ktype
kobject对象被关联到一种特殊的类型,即ktype。ktype由kobj_type结构体表示,定义于
struct kobj_type {
void (*release)(struct kobject *);
struct sysfs_ops * sysfs_ops;
struct attribute ** default_attrs;
};
ktype是为了描述一族kobject所具有的普遍特性。因此,不再需要每个kobject都分别定义自己的特性,而是将这些普遍的特性在ktype结
构体中一次定义,然后所有“同类”的kobject都能共享一样的特性。release指针指向在kobject引用计数减至0时要被调用的析构函数。该
函数负责释放所有kobject使用的内存和其它相关清理工作。sysfs_ops变量指向sysfs_ops结构体。该结构体描述了sysfs文件读写
时的特性。default_attrs指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认属性。属性给定了对象的特征,
如果该kobject被导出到sysfs中,那么这些属性都将相应的作为文件而导出。
17.3 kset
kset是kobject对象的集合体。把它看成一个容器,可将所有相关的kobject对象,比如“全部的块设备”置于同一位置。kset可把 kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性,它们之间的重要区别在于:具有相同ktype的kobject可 以分到不同的kset中(同一个kset中的kobject是否具有相同的ktype吗???)。kobject的kset指针指向相应的kset集合。
struct kset {
struct subsystem * subsys;
struct kobj_type * ktype;
struct list_head list;
struct kobject kobj;
struct kset_hotplug_ops * hotplug_ops;
};
其中ktype指针指向集合(kset)中的kobject对象的类型(ktype),list连接该集合(kset)中所有的kobject对象。
kobj指向的kobject对象代表了该集合的基类。hotplug_ops指向一个用于处理集合中kobject对象的热拔插操作的结构体。
subsys指针指向该结构体相关的struct subsystem结构体。
17.4 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进行并发访问保护。
17.6 管理和操作kobject
17.7 引用计数
kobject的主要功能之一就是为我们提供了一个统一的引用计数系统。初始化后,kobject的引用计数设置为1。只要引用计数不为0,那么该 对象就会继续保留在内存中,也可以说是被“钉”住了,任何包含对象引用的代码首先要增加该对象的引用计数,当代码结束后则减少它的引用计数。当引用计数减 为0时,对象便可以被销毁,同时相关内存也都被释放。
增加一个引用计数可通过kobject_get()函数完成:
struct kobject * kobject_get(struct kobject *kobj);
该函数正常情况下返回一个指向kobject的指针,如果失败,则返回NULL指针。
减少引用计数通过kobject_put()完成:
void kobject_put(struct kobject *kobj);
17.8 sysfs
sysfs文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject对象层次结构的视图。帮助用户可以一个简单文件系统的方式来观察系统中 各种设备的拓扑结构。借助属性对象,kobject可用导出文件的方式,将内核变量提供给用户读取或写入。
sysfs的诀窍是把kobject对象与目录项紧密联系起来,这点是通过kobject中的dentry(directory entry)字段实现的。回忆一下12章,dentry结构体表示目录项,通过连接kobject到指定的目录项上,无疑方便的将kobject映射到该 目录上。好了,kobject其实已经形成了一棵树了——就是我们心爱的对象模型体系。
sysfs的根目录下包含了七个子目录:block、bus、class、devices、firmware、module和power。block目录 下的每个子目录都对应着系统中的一个块设备。反过来,每个目录下又都包含了该块设备的所有分区。bus目录提供了一个系统总线视图。class目录包含了 以高层功能逻辑组织起来的系统设备视图。devices目录是系统中设备拓扑结构视图,它直接映射出了内核中设备结构体的组织层次。firmware目录 包含一些诸如ACPI、EDD、EFI等低层子系统的特殊树。power目录包含了系统范围的电源管理数据。
17.8.1 sysfs中添加和删除kobject
仅仅初始化一个kobject是不能自动将其导出到sysfs中的,想要把kobject导入sysfs,需要用到函数kobject_add():
int kobject_add(struct kobject *kobj);
kobject在sysfs中的位置取决于kobject在对象层次结构中的位置。如果kobject的父指针被设置,那么在sysfs中kobject 将被映射为其父目录下的子目录,如果parent没有设置,那么kobject将被映射到kset->kobj中的子目录。两者都未设置,映射为 sysfs下的根级目录。
17.8.2 向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字段则描述了如何使用它们。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表示的属性结构体变量中。