kobject
Linux内核中有大量的驱动和设备,而这些驱动和设备往往具有类似的结构,根据面向对象的思想,我们就可以将这些共同的部分提取为父类,这个父类就是kobject,也就是驱动编程中使用的.ko文件的由来,而三大类设备驱动都需要包含这个kobject结构,也就是"继承"自kobject。一个kobject对象就对应sys目录中的一个设备,也可以理解为一个kobject就对应sys目录中的一个目录。
内核源码中的kobject结构定义如下
struct kobject {
/**
* 指向容器名称。
*/
char * k_name;
/**
* 如果容器名称不超过20个字符,就存在这里。
*/
char name[KOBJ_NAME_LEN];
/**
* 容器的引用计数。
*/
struct kref kref;
/**
* 用于将kobject插入某个链表。
*/
struct list_head entry;
/**
* 指向父kobject
*/
struct kobject * parent;
/**
* 指向包含的kset,kset是同类型的kobject结构的一个集合体。
*/
struct kset * kset;
/**
* 指向kobject的类型描述符。
*/
struct kobj_type * ktype;
/**
* 指向与kobject对应的sysfs文件的dentry数据结构。
*/
struct dentry * dentry;
};
初始化kobject
void kobject_init(struct kobject * kobj)
{
// 初始化引用计数为1
kref_init(&kobj->kref);
atomic_set(&kref->refcount,1);
INIT_LIST_HEAD(&kobj->entry);
// 获取kobj的kset对象,如果kset不为空,就会将对应kset的kobject引用计数加1
kobj->kset = kset_get(kobj->kset);
return k ? to_kset(kobject_get(&k->kobj)) : NULL;
}
添加kobject到kset
int kobject_add(struct kobject * kobj)
// 将此对象的引用计数加1
kobj = kobject_get(kobj)
// 将父对象的引用计数加1
parent = kobject_get(kobj->parent);
//设置对象的父对象,如果原本就用父对象,就不变,否则就把父对象设置成对应kset的对象,还要通过entry,将该对象加入到对应kset的list链表中
if (kobj->kset)
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
list_add_tail(&kobj->entry,&kobj->kset->list);
kobj->parent = parent;
create_dir(kobj);
//创建该对象对应的目录
sysfs_create_dir(kobj);
// 如果该对象的父对象不为空,则创建的目录就位于父对象目录下面,否则创建的目录就位于sysfs根目录下面
if (kobj->parent)
parent = kobj->parent->dentry;
else if (sysfs_mount && sysfs_mount->mnt_sb)
parent = sysfs_mount->mnt_sb->s_root;
// 在parent目录下,创建一个名为kobject_name(kobj)的目录
create_dir(kobj,parent,kobject_name(kobj),&dentry);
//设置该对象的dentry
kobj->dentry = dentry;
// 创建kobject的属性文件
populate_dir(kobj);
struct kobj_type * t = get_ktype(kobj);
if (k->kset && k->kset->ktype)
return k->kset->ktype;
else
return k->ktype;
if (t && t->default_attrs)
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++)
//在kobj目录下,创建属性文件
sysfs_create_file(kobj,attr)
kobject_add主要作用有两个
(1)设置kobject的父对象,如果kobject没有父对象,就把父对象设置为父kset的对象。
(2)在父对象目录下创建kobject对应的目录和属性文件。
注册kobject
int kobject_register(struct kobject * kobj)
{
kobject_init(kobj);
kobject_add(kobj);
return error;
}
kobject_register首先调用kobject_init初始化对象,然后调用kobject_add添加kobject到kset
kset
kset表示一组kobject的集合,kobject通过kset组织成层次化的结构,所有属于该kset的kobjetc结构的parent指针指向kset包含的kobject对象,构成一个父子层次关系,这些kobject可以是不同或相同的类型(kobj_type)。同时一个kset也是一个kobject,kset也含有自己的kobject。sysfs中的设备组织结构很大程度上都是根据kset进行组织的,比如"/sys/drivers"目录就是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录由kobject表示。比如在平台设备模型中,当我们注册一个设备或驱动到平台总线,其实是将对应的kobject挂接到platform总线的kset上,每种总线都是维护两条链表(两个kset),一条用于链接挂接在上面的驱动(驱动kset),一条用于链接挂接在上面的设备(设备kset)。
struct {
/**
* 所属子系统。
*/
struct subsystem * subsys;
/**
* 所包含的kobjec的类型。
*/
struct kobj_type * ktype;
/**
* 第一个kobject节点。
*/
struct list_head list;
/**
* 嵌入的kobject
*/
struct kobject kobj;
/**
* 用于处理kobject结构的过滤和热插拨操作的回调函数表。
*/
struct kset_hotplug_ops * hotplug_ops;
};
list_head还是那个用来挂在链表上的结构,包含在一个kset的所有kobject构成了一个双向循环链表,list_head就是这个链表的头部,这个链表用来连接第一个和最后一个kobject对象,第一个kobject使用entry连接kset集合以及第二个kobject对象,第二个kobject对象使用entry连接第一个kobject对象和第三个kobject对象,依次类推,最终形成一个kobject对象的链表
kobject是归属于该kset的所有的kobject的共有parent,这个parent就是体现内核设备组织结构的关键,同时,kset的引用计数就是内嵌的kobject对象的引用次数。从这里就可以看出来kset也是继承了kobject。
注册kset
int kset_register(struct kset * k)
{
kset_init(k);
kobject_init(&k->kobj);
INIT_LIST_HEAD(&k->list);
return kset_add(k);
if (!k->kobj.parent && !k->kobj.kset && k->subsys)
k->kobj.parent = &k->subsys->kset.kobj;
return kobject_add(&k->kobj);
}
kset_register的作用有两个
(1)初始化数据结构,主要就是初始化对应kobject。
(2)设置kset的父对象,也就是设置对应kobject的父kobject,如果父对象为空,对应kobject所属的kset为空,并且kset对应的subsys不为空,就把父对象设置为&k->subsys->kset.kobj。从这里可以看到kobject属于设备的最底层结构,多个kobject可以属于同一个kset,这是通过kobject的parent字段指向kset的kobject来连接的,kset可以看做kobject的继承类,所以kset本质上也是一个kobject,subsystem是kset的继承类,所以subsystem本质上是一个kset,同时也是一个kobject。subsystem的子kset通过对象的parnet字段指向父subsystem的kobj来关联起来的。
subsystem
一个子系统下可以包含多种kset,子系统通常显示在sysfs分层结构中的顶层,包含block_subsys、devices_subsys以及各种总线等子系统,对应于sys/block、sys/devices等目录。
struct subsystem {
/**
* 下层对象集合。
*/
struct kset kset;
/**
* 访问子系统所用的读写信号量。
*/
struct rw_semaphore rwsem;
};
子系统注册
subsystem_register(struct subsystem * s)
//初始化子系统
subsystem_init(s);
init_rwsem(&s->rwsem);
kset_init(&s->kset);
//添加子系统对应的kset
kset_add(&s->kset));
if (!s->kset.subsys)
s->kset.subsys = s;
subsystem_register主要包含两个内容
(1)初始化子系统对象
(2)调用kset_add来添加子系统,调用subsystem_register时,如果对应kobject的父对象为空,kset_add就会在/sys下创建目录。
subsystem、kset、kobject的关系如下图所示,黑色连线代表包含关系,红色连线代表指向关系: