在Linux设备模型中,kobject是它的基础结构。其数据结构定义在kernel-4.9/include/linux/kobject.h。
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct kernfs_node *sd; /* sysfs directory entry */
struct kref kref;
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
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;
};
name, kobject的名称,它将以一个目录的形式出现在sysfs文件系统中
kref对象,用于引用计数管理。kref_init()接口用于初始化kref接口,kref_get()用于增加相关的引用计数,而kref_put则用于减少引用计数,当没有剩下的引用后,对象会被释放。kref的相关定义在kernel-4.9/include/linux/kref.h中。
kset的指针,表征kobject归属的对象集。
kobj_type,用于描述kobject的对象类型。
kobject与sysfs紧密关联。内核中的每个对象实例都有一个sysfs的代表。
kobject的使用
内核代码几乎不会去创建一个单独的kobject对象。它在使用时,会被嵌入到其他的结构中。这个和之前提到的双向链表 list_head的用法一样。要找到对应包含kobject的结构,需要用到之前提到宏container_of。
初始化:
首先要将整个kobject设置为0,这通常使用memset函数。之后调用kobject_init()函数,再通过kobject_set_name设个名字,这个名字会在sysfs入口中使用。kobject的创建者需要直接或者间接设置的成员有:ktype、kset和parent。
以下是kobject和引用计数相关一些函数:
/* 调用成功将增加kobject的引用计数,并返回指向kobject的指针。
* 如果kobject已经处于被销毁的过程中,则调用失败,kobject_get返回NULL。
* 必须检查返回值,否则可能会产生麻烦的竟态。
*/
extern struct kobject *kobject_get(struct kobject *kobj);
/* 调用后减少引用计数,并在可能的情况下释放该对象。
* 请记住kobject_init设置引用计数为1,所以当创建kobject时,
* 如果不再需要初始的引用,就要调用响应的kobject_put函数。
*/
extern void kobject_put(struct kobject *kobj);
在许多情况下,kobject中的引用计数不足以防止竞态的产生。举个例子,kobject的存在需要创建它的模块继续存在。当kobject继续被使用时,不能卸载该模块。以下是cdev结构中的引用计数的实现:
struct kobject *cdev_get(struct cdev *p) {
struct module *owner = p->owner;
struct kobject *kobj;
if (owner && !try_module_get(owner)) {
return NULL;
}
kobj = kobject_get(&p->kobj);
if (!kobj) {
module_put(owner);
}
return kobj;
}
创建cdev结构的引用时,也需要创建包含它的模块的引用。因此,cdev_get使用try_module_get去增加模块的使用计数。如果操作成功,使用kobject_get增加kobject的引用计数。当然这个操作也可能会失败,因此代码需要检测kobject_get的返回值。如果失败,则释放对模块的引用计数。
当kojbect的引用计数为0时,会异步的调用对应的release方法释放资源。每一个kobject都必须有一个release方法,并且kobject在该方法被调用前必须保持不变(处于稳定状态)。release被包含在kobj_type中,如下:
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);
};
kobj_type是kobject的一个成员。在初始化时作为参数传入:
extern void kobject_init(struct kobject *kobj, struct kobj_type *ktype);