一.数据结构设计
0. 需要被管理的实体实际上很杂,包括设备,驱动,总线,类型,块设备,电源等等...迫切需要统一管理。
1. kobject代表每一个被管理实体,很显然的,这些实体可以带有一个或者多个属性。
2. 这些属性由attribute表示,由于被管理的实体不同,可能还会互相嵌套,因此很难给出一个明确的attribute的定义,因此使用了list_head的设计方式,将它仅仅作为一个锚点,真实的数据存在于它附近的内存区域,通过container_of宏来得到。(之所以可以这么做,得益于冯诺依曼机器的连续存储)
3. 另外,由于kobject也是一个锚点,并不携带真实数据,因此kobject和attribute之间也不便直接建立关系,而是通过kobj_type结构体解除耦合的。
4. 为了将被管理实体归类,设计了kset数据结构,管理一类kobject
5. kset同时也是一个kobject,这就实现了一个组合模式。kobject的精化最终在这里体现。
二.用什么方式表现
1. 由于kobject将所有被管理实体组织成一个树型结构,因此任意可以表示树型结构的方式都可以采用。
2. linux并不像windows导出很多操作接口(比如注册表,文件等),它基本只导出文件接口,也就是一个vfs接口,同时linux的文件系统组织是树型的,且实现了mount扩展机制,将不同类型的文件系统子树嫁接在根的任意子节点(需要是目录)上。
3. 很方便为kobject实现一个文件系统,然后mount到某一处。
4. 这个文件系统类型就是sysfs,一般处于/目录下的/sysfs目录中。
三.实现
1. kobject的定义:
struct kobject {
const char *name; //名称
struct list_head entry; //
struct kobject *parent; //kobject组成链表,便于查找
struct kset *kset; //此kobj所属的kset
struct kobj_type *ktype; //kobj_type解耦了kobj和attr
struct sysfs_dirent *sd; //sysfs中的组织结构
struct kref kref; //引用计数
...
};
2. kobj_type的定义:
struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops; //该kobj_type所属的kobject的所有attribute的总的store和show方法
struct attribute **default_attrs; //此type的属性们
};
3. kset的定义:
struct kset {
struct list_head list; //属于此kset的kobj们
...
struct kobject kobj; //kset本身也是一个kobject,实现一个类别
struct kset_uevent_ops *uevent_ops; //实现向用户态的通知机制
};
4. 真实attribute的定义:
struct edac_pci_dev_attribute {
struct attribute attr; //所属的attribute结构锚点
void *value; //真实的,单独的attribute的值
ssize_t(*show) (void *, char *); //真实的,单独的attribute的show
ssize_t(*store) (void *, const char *, size_t);//真实的,单独的attribute的store
};
5. 每一个kobject在sysfs中都实现为一个目录,kobject是树型的,该树和sysfs中的目录树真切地对应。
6. 一个kobject的每一个attribute在sysfs中都实现为该kobject对应目录下的一个文件,所有的attribute统一由该kobject的kobj_type管理。
7. sysfs文件系统中每一个文件代表它所属目录的kobject的一个属性,拥有读/写方法,即show/store。
8. 所有处于同一目录下的attribute的show/store统一由该目录所属kobject的kobj_type的show/store来分发,比如对于/sys/devices目录:
8.1.在注册一个顶层设备(比如总线等)时,均会将该device结构体的kobj的ktype初始化为device_ktype:
static struct kobj_type device_ktype = {
.release = device_release,
.sysfs_ops = &dev_sysfs_ops,
};
8.2.所有的8.1中注册的device的属性读写操作(show/store)全部通过以下的dev_sysfs_ops代理:
static struct sysfs_ops dev_sysfs_ops = {
.show = dev_attr_show,
.store = dev_attr_store,
};
static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr, char *buf)
{
struct device_attribute *dev_attr = to_dev_attr(attr);
struct device *dev = to_dev(kobj);
ret = dev_attr->show(dev, dev_attr, buf);
}
8.3.对于每一个单独的属性,要单独定义,比如对于devt属性:
static struct device_attribute devt_attr =
__ATTR(dev, S_IRUGO, show_dev, NULL);
定义完之后,通过device_create_file加入sysfs文件系统:
device_create_file(dev, &devt_attr);
8.4.当dev_attr_show中调用dev_attr->show时,执行流被路由到show_dev函数:
static ssize_t show_dev(struct device *dev, struct device_attribute *attr, char *buf)
{
return print_dev_t(buf, dev->devt);
}
每一个kobject代表一个目录,其sd字段将kobject树转换成了sysfs文件系统的文件目录树。注意,sysfs使用dentry中的d_fsdata字段和kobject解除了耦合,所有的操作只有在接口层面上操作dentry和inode,进入后就会通过dentry的d_fsdata字段和inode的i_private字段转换为kobject机制的结构,比如sysfs_dirent结构。
四.和Windows管理方式的对比
1. windows使用GUID来管理设备,驱动,总线,类型,块设备,电源等等...它本身就很统一。
2. windows使用注册表来管理这些,例如HKEY_LOCAL_MACHINE\SYSTEM\ControlSet001\Control\Class键下面保存有各种类型的被管理实体,对应于linux的sysfs的根目录。
3. GUID是个无结构的大数,通过复杂的算法生成,只求唯一性,GUID之间没有关联性,耦合性更低。kobject恰恰相反,它在本机范围内构建了一个树型的被管理实体结构。
4. sysfs便于查找,而GUID某种意义上只能遍历,消耗很大,这就是注册表随着时间增加会拖慢系统速度的原因之一。
5. GUID方式的管理也支持属性,并且节点也可以包含到其它节点的链接,这点和sysfs很相似。
6. 微软希望使用GUID来管理所有它能管理的实体,甚至包括office文档,然而这被证明不是一个好主意,虽然一个大数耦合性很低,内聚性很强,然而内聚性过强也会导致不能和外界通信。
7. 总之,GUID的方式没有做到恰到好处。
五.总结
1. Linux使用kobject-一个组合模式的数据结构。
2. Windows使用GUID-一个无耦合全内聚的数字。
3. 处处都存在设计模式。
3.1.面向对象的思想(OO)在kobject的设计中表现的很淋漓,具体就在kobj_type,虽然使用了type这个词,但实际上就是class,一个kobject的具体attitude就是一个kobj_type这个class的一个实例,由于kobject是表征凌乱不堪的实体的,因此kobject肯定区分了不同的类型,每一种类型的“操作attitude的方式(方法)”肯定相同,因此出现了kobj_type这个数据结构来解除分类的kobject与无类别的attitude之间的耦合。
3.2.锚点的设计方式其实在我们日常生活中也很普遍,比如身份证就代表某个人,人与人不同,然而身份证的格式是一样的,个人就是杂乱不堪的被管理实体,身份证就是kobject。