现在的位置: 首页 > 综合 > 正文

kobject和kset

2013年10月06日 ⁄ 综合 ⁄ 共 7523字 ⁄ 字号 评论关闭

1 Kobject

1.1 简介

Kobject 是Linux 2.6 引入的新的设备管理机制,在内核中由struct kobject数据结构 进行描述通过这个数据结构使所有设备在底层都具有统一的接口,kobject提供基本的对象管 理,是构成Linux2.6设备模型的核心结构,它与sysfs文件系统紧密关联,每个在内核中注 册的kobject对象都对应于sysfs文件系统中的一个目录。Kobject是组成设备模型的基本结 构。但是,它比较低调,从不单独使用,都是嵌套在别的数据结构中。类似于C++中的基 类,它嵌入于更大的对象的对象中–所谓的容器–用来描述设备模型的组件。如bus, devices, drivers 都是典型的容器。这些容器就是通过kobject连接起来了,形成了 一个树状结构。这个树状结构就与/sys相对应。

kobject 结构为一些大的数据结构和子系统提供了基本的对象管理,避免了类似机能的重 复实现。这些机能包括

  • 对象引用计数.
  • 维护对象集合.
  • 对象上锁.
  • 在用户空间的表示.

1.2 定义

Kobject结构定义为:

struct kobject {
const char *name; /* 指向设备名称的指针 */
struct list_head entry; /* 挂接到所在kset中去的单元 */
struct kobject *parent; /* 指向父对象(kobject)的指针 */
struct kset *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;
};

其中,重要的变量已经加了注释,这里再简要介绍一下:

  • kref:
    kref域表示该对象引用的计数,内核通过kref实现对象引用计数管理,内核提供两 个函数kobject_get()、kobject_put() 分别用于增加和减少引用计数,当引用计数为 0时,所有该对象使用的资源释放。
  • Ktype:
    域是一个指向kobj type结构的指针,表示该对象的类型。
  • parent
    指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构, 并且可以将对各对象间的关系表现出来,就如你看到的,这便是sysfs的真正面目: 一个用户空间的文件系统,用来表示内核中kobject对象的层次结构。

1.3 相关函数

  • void kobject_init(struct kobject * kobj) kobject初始化函数。
  • int kobject_set_name(struct kobject *kobj, const char *format, …) 设置指定kobject的名称。
  • struct kobject *kobject_get(struct kobject *kobj) 将kobj 对象的引用计数加1,同时返回该对象的指针。
  • void kobject_put(struct kobject * kobj) 将kobj对象的引用计数减1,如果引用计数降为0,则调用kobject release()释放该 kobject对象。
  • int kobject_add(struct kobject * kobj) 将kobj对象加入Linux设备层次。挂接该kobject对象到kset的list链中,增加父目录 各级kobject的引用计数,在其 parent指向的目录下创建文件节点,并启动该类型内 核对象的hotplug函数。
  • int kobject_register(struct kobject * kobj) kobject注册函数。通过调用kobject init()初始化kobj,再调用kobject_add()完 成该内核对象的注册。
  • void kobject_del(struct kobject * kobj) 从Linux设备层次(hierarchy)中删除kobj对象。
  • void kobject_unregister(struct kobject * kobj) kobject注销函数。与kobject register()相反,它首先调用kobject del从设备层次 中删除该对象,再调用kobject put()减少该对象的引用计数,如果引用计数降为0, 则释放kobject对象。

2 Kobj type

kobject对象被关联到一种特殊的类型,即ktype。ktype由kobj_type结构体表示,定义于 <linux/kobject.h>中:

struct kobj_type {
void (*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
};
  • release指针 指向在kobject引用计数减至0时要被调用的析构函数。该函数负责释放 所有kobject使用的内存和其它相关清理工作。
  • ktype

是为了描述一族kobject所具有的普遍特性。因此,不再需要每个kobject都分别定 义自己的特性,而是将这些普遍的特性在ktype结构体中一次定义,然后所有“同类”的 kobject都能共享一样的特性。

  • sysfs_ops

指向sysfs操作表和一个sysfs文件系统缺省属性列表。Sysfs操作表包括两 个函数store()和show()。当用户态读取属性时,show()函数被调用,该函数编码指定属 性值存入buffer中返回给用户态;而store()函数用于存储用户态传入的属性值。

  • default_attrs

指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认 属性。属性给定了对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应 的作为文件而导出。其定义如下:

struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};

它以文件的形式输出到sysfs的目录当中。在kobject对应的目录下面。文件 名就是name。文件读写的方法对应于kobj_type中的sysfs_ops。

3 kset

3.1 简介

kset 最重要的是建立上层(sub-system)和下层的(kobject)的关联性。kobject 也会利用 它了分辨自已是属于那一個类型,然後在/sys 下建立正确的目录位置。而kset 的优先权 比较高,kobject会利用自已的*kset 找到自已所属的kset,並把*ktype 指定成該kset下 的ktype,除非沒有定义kset,才会用ktype來建立关系。Kobject通过kset组织成层次化 的结构,kset是具有相同类型的kobject的集合,在内核中用kset数据结构表示。

3.2 定义

/**
* 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; /**< Spinlock */
struct kobject kobj; /**< 这个KSet中嵌入的 kobject */
struct kset_uevent_ops *uevent_ops; /**< 操作函数 */
};
  • list: 包含在kset中的所有kobject被组织成一个双向循环链表的表头。
  • list_lock: 遍历这个kset中的所有kobject所需持有的锁。
  • kobject: 数据结构中内嵌的kobject对象(由kobj域表示),所有属于这个kset 的kobject对象的parent域均指向这个内嵌的对象。此外,kset的引用计数实际上就是内嵌的 kobject对象的引用计数。

3.3 相关函数

  • kset_init(): 完成指定kset的初始化
  • kset_get()和kset_put(): 分别增加和减少kset对象的引用计数。
  • Kset_add()和kset_del(): 函数分别实现将指定keset对象加入设备层次和从其中删除
  • ksetregister() 和 ksetunregister(): 完成 kset的注册和注销
在建立linxu设备模型中kobject是一个重要的而且基础的数据结构,如果向内核注册一个kobject就相当于在sysfs文件系统下建立一个目录 .kobject又被嵌入到一个更大结构kset当中 ,在设备模型中 总线,设备,驱动就是典型的ket模型.
下面引用ULK3中的一张图来看看:
这个图很清晰的给我们展示了一个驱动注册到pci总线上后.sys下的目录结构. 从这里我们我们可以看出使用sysfs文件系统建立起来的设备模型是很有层次的.
pci和drivers都是一个ket组成,serial由kobj构成, 在serial这个目录下面有一个属性文件new-id .
 
注册kobject对象
我们这里就从创建并注册kobject对象开始吧,
636 struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
637 {
638         struct kobject *kobj;
639         int retval;
640
641         kobj = kobject_create();
642         if (!kobj)
643                 return NULL;
644
645         retval = kobject_add(kobj, parent, "%s", name);
646         if (retval) {
647                 printk(KERN_WARNING "%s: kobject_add error: %d/n",
648                        __func__, retval);
649                 kobject_put(kobj);
650                 kobj = NULL;
651         }
652         return kobj;
653 }
第641行, 在内存中分配一个kobject, 并调用kobject_init()初始化这个kobject. 第645行的函数就是要把这个kobject注册到内核中了.
看看kobject_add()实现流程:
kobject_add()->kobject_add_varg()->kobject_set_name_vargs()->kobject_add_internal()->create_dir()->sysfs_create_dir() ; 从这个流程可以看到注册一个kobject实际上就是在sysfs文件系统中创建一个目录.前面几个函数都是较简单的,这里主要看看kobject_add_internal()函数:
158 static int kobject_add_internal(struct kobject *kobj)
159 {
160         int error = 0;
161         struct kobject *parent;
162
163         if (!kobj)
164                 return -ENOENT;
165
166         if (!kobj->name || !kobj->name[0]) {
167                 pr_debug("kobject: (%p): attempted to be registered with empty "
168                          "name!/n", kobj);
169                 WARN_ON(1);
170                 return -EINVAL;
171         }
172
173         parent = kobject_get(kobj->parent);
174
175         /* join kset if set, use it as parent if we do not already have one */
176         if (kobj->kset) {
177                 if (!parent)
178                         parent = kobject_get(&kobj->kset->kobj);
179                 kobj_kset_join(kobj);
180                 kobj->parent = parent;
181         }
182
183         pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'/n",
184                  kobject_name(kobj), kobj, __func__,
185                  parent ? kobject_name(parent) : "<NULL>",
186                  kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
187
188         error = create_dir(kobj);
189         if (error) {
190                 kobj_kset_leave(kobj);
191                 kobject_put(parent);
192                 kobj->parent = NULL;
193
194                 /* be noisy on error issues */
195                 if (error == -EEXIST)
196                         printk(KERN_ERR "%s failed for %s with "
197                                "-EEXIST, don't try to register things with "
198                                "the same name in the same directory./n",
199                                __func__, kobject_name(kobj));
200                 else
201                         printk(KERN_ERR "%s failed for %s (%d)/n",
202                                __func__, kobject_name(kobj), error);
203                 dump_stack();
204         } else
205                 kobj->state_in_sysfs = 1;
206
207         return error;
208 }
第166行代码是判断kobj的名字,我们的目录是要有名字的. 173行代码,增加kobj父亲的引用计数
第176-180行代码, 如果kobj设置了kset, 在kobj未指定父对象的情况下,把ket->kobj设为这个kobj的父亲,并将kobj对象添加到kset的kobj list中.
第188行代码create_dir()在以前已经分析过了,就是在sysfs下创建一个目录文件撒.
 
注册kset对象
在分析了kobj的注册以后再来分析ket的注册就比较简单了.
Kset创建并注册流程:
kset_create_and_add()-> kset_create()->kset_register()->kobject_add_internal()->kobject_uevent();
其实ket的注册流程和kobj的注册流程基本上都是一样的,ket的注册实际上就把它嵌套的kobj注册到内核中了,同样也会在sysfs相应位置建立一个目录.
但与注册kobj不同的是, 在注册一个kset时会调用函数kobject_uevent(&k->kobj, KOBJ_ADD) 通过netlink把KOBJ_ADD事件发送到用户层的udev ,在udev收到内核发的这个消息时就会执行相应的事件了.

抱歉!评论已关闭.