kobject是Linux设备模型的最底层数据结构,它代表一个内核对象。
kobject结构体定义在include/linux/kobject.h文件中:
struct kobject {
const char *name; //这个内核对象的名字,在sysfs文件系统中,name将以一个目录的形式出现
struct list_head entry; //用于将该内核对象链接进其所属的kset的内核对象链表
struct kobject *parent;//代表该内核对象的父对象,用于构建内核对象的层次结构
struct kset *kset; //kset是该内核对象所属的“内核对象集合”
struct kobj_type *ktype; //ktype是该内核对象的sysfs文件系统相关的操作函数和属性。
struct kernfs_node *sd; /* sysfs directory entrysd表示该内核对象对应的sysfs目录项。 */
struct kref kref;//kref的核心数据是一个原子型变量,用于表示该内核对象的引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
struct delayed_work release;
#endif
unsigned int state_initialized:1;//表示该内核对象是否已经进行过了初始化,1表示已经初始化过了
unsigned int state_in_sysfs:1;//表示该内核对象是否已经在sysfs文件系统中建立一个入口点
unsigned int state_add_uevent_sent:1;//表示加入内核对象时是否发送uevent事件
unsigned int state_remove_uevent_sent:1;//表示删除内核对象时是否发送uevent事件
unsigned int uevent_suppress:1;//表示内核对象状态发生变化时,是否发送uevent事件
};
kobject初始化分析,从kobject_init_and_add
函数开始看,该函数完成对kobject的初始化,建立kobject的层次结构,并将kobject添加到sysfs文件系统中,该函数定义在lib/kobject.c文件中,其内容如下:
/**
* kobject_init_and_add - initialize a kobject structure and add it to the kobject hierarchy
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
* @parent: pointer to the parent of this kobject.
* @fmt: the name of the kobject.
*
* This function combines the call to kobject_init() and
* kobject_add(). The same type of error handling after a call to
* kobject_add() and kobject lifetime rules are the same here.
*/
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
va_list args;
int retval;
kobject_init(kobj, ktype); //对kobject进行初始化
va_start(args, fmt); //va_start用于处理可变参数
retval = kobject_add_varg(kobj, parent, fmt, args); //将kobject添加到kobject层次结构中
va_end(args);
return retval;
}
其中,kobject_init
函数,其定义如下:
/**
* kobject_init - initialize a kobject structure
* @kobj: pointer to the kobject to initialize
* @ktype: pointer to the ktype for this kobject.
*
* This function will properly initialize a kobject such that it can then
* be passed to the kobject_add() call.
*
* After this function is called, the kobject MUST be cleaned up by a call
* to kobject_put(), not by a call to kfree directly to ensure that all of
* the memory is cleaned up properly.
*/
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
{
char *err_str;
if (!kobj) {
err_str = "invalid kobject pointer!";
goto error;
}
if (!ktype) { //如果ktype为空,则返回一个错误
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) { //如果kobject已经被初始化过,则打印错误信息
/* do not error out as sometimes we can recover */
printk(KERN_ERR "kobject (%p): tried to init an initialized "
"object, something is seriously wrong.\n", kobj);
dump_stack();
}
kobject_init_internal(kobj); //调用kobject_init_internal函数
kobj->ktype = ktype; //将传递过来的ktype赋值给kobj->ktype
return;
error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}
其中调用了kobject_init_internal
函数,该函数定义在lib/kobject.c文件中,其定义如下:
static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);
INIT_LIST_HEAD(&kobj->entry);
kobj->state_in_sysfs = 0;
kobj->state_add_uevent_sent = 0;
kobj->state_remove_uevent_sent = 0;
kobj->state_initialized = 1;
}
kobject_init_internal
函数完成对kobject成员变量的初始化,其中对kobj->kref
即引用计数的初始化,这是通过kref_init
函数完成的,该函数定义在include/linux/kref.h文件中,其定义如下:
struct kref {
atomic_t refcount;
};
/**
* kref_init - initialize object.
* @kref: object in question.
*/
static inline void kref_init(struct kref *kref)
{
atomic_set(&kref->refcount, 1);
}
引用计数的核心变量是一个原子变量,初始化为1。
回到kobject_init_and_add
函数中,调用kobject_add_varg
函数,该函数定义在lib/kobject.c文件中,其内容如下:
static __printf(3, 0) int kobject_add_varg(struct kobject *kobj,
struct kobject *parent,
const char *fmt, va_list vargs)
{
int retval;
retval = kobject_set_name_vargs(kobj, fmt, vargs);//解析可变参数,设置kobject.name
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n");
return retval;
}
kobj->parent = parent; //kobject.parent为parent
return kobject_add_internal(kobj);
}
其中调用kobject_add_internal
函数,该函数定义在lib/kobject.c文件中,其内容如下:
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
if (!kobj)
return -ENOENT;
if (!kobj->name || !kobj->name[0]) { //检查是否已经设置kobject.name,如果没有设置则返回错误
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
return -EINVAL;
}
parent = kobject_get(kobj->parent); //kobject_get函数取得kobject.parent
/* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
//如果parent为空,则将kset.kobject设置为kobject.parent
parent = kobject_get(&kobj->kset->kobj);
//如果kobject.kset不为空,则调用kobj_kset_join函数,将kobject.entry链接进kobject.kset.list中
kobj_kset_join(kobj);
kobj->parent = parent;
}
pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",
kobject_name(kobj), kobj, __func__,
parent ? kobject_name(parent) : "<NULL>",
kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");
error = create_dir(kobj);//创建kobject在sysfs文件系统中对应的目录结构
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL;
/* be noisy on error issues */
if (error == -EEXIST)
WARN(1, "%s failed for %s with "
"-EEXIST, don't try to register things with "
"the same name in the same directory.\n",
__func__, kobject_name(kobj));
else
WARN(1, "%s failed for %s (error: %d parent: %s)\n",
__func__, kobject_name(kobj), error,
parent ? kobject_name(parent) : "'none'");
} else
kobj->state_in_sysfs = 1; //在sysfs文件系统中创建目录结构成功,则将 kobj->state_in_sysfs设置为1
return error;
}
其中,kobject_get
函数取得kobject.parent
,该函数定义在lib/kobject.c文件中,其内容如下:
/**
* kobject_get - increment refcount for object.
* @kobj: object.
*/
struct kobject *kobject_get(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized)
WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
"initialized, yet kobject_get() is being "
"called.\n", kobject_name(kobj), kobj);
kref_get(&kobj->kref);
}
return kobj;
}
该函数除了返回对应的kobject,还通过调用kref_get
函数,增加该kobject的引用计数。
kobject_add_internal
函数中,如果kobject.kset
不为空,则调用kobj_kset_join
函数,将kobject.entry
链接进kobject.kset.list
中
kobject_add_internal
函数中调用create_dir
函数创建kobject在sysfs文件系统中对应的目录结构,该函数定义在lib/kobject.c文件中,其内容如下:
static int create_dir(struct kobject *kobj)
{
const struct kobj_ns_type_operations *ops;
int error;
error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
if (error)
return error;
error = populate_dir(kobj);
if (error) {
sysfs_remove_dir(kobj);
return error;
}
/*
* @kobj->sd may be deleted by an ancestor going away. Hold an
* extra reference so that it stays until @kobj is gone.
*/
sysfs_get(kobj->sd);
/*
* If @kobj has ns_ops, its children need to be filtered based on
* their namespace tags. Enable namespace support on @kobj->sd.
*/
ops = kobj_child_ns_ops(kobj);
if (ops) {
BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
BUG_ON(ops->type >= KOBJ_NS_TYPES);
BUG_ON(!kobj_ns_type_registered(ops->type));
sysfs_enable_ns(kobj->sd);
}
return 0;
}
create_dir()
在sysfs中创建kobj对应的目录,populate_dir()
创建kobj中默认属性对应的文件。create_dir()
正是调用populate_dir()
实现的。该函数定义在lib/kobject.c文件中,其内容如下:
/*
* populate_dir - populate directory with attributes.
* @kobj: object we're working on.
*
* Most subsystems have a set of default attributes that are associated
* with an object that registers with them. This is a helper called during
* object registration that loops through the default attributes of the
* subsystem and creates attributes files for them in sysfs.
*/
static int populate_dir(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
struct attribute *attr;
int error = 0;
int i;
if (t && t->default_attrs) {
for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);
if (error)
break;
}
}
return error;
}
至此,先后分析了kobject_add_internal
函数把kobj加入已有的结构;相应的kobject_add_varg
函数;相应的kobject_init_and_add
函数。可以看到,kobject_init_and_add
函数完成了对kobject初始化,建立了kobject的层次结构,并将kobject添加到sysfs文件系统中。
kobject的属性
kobject.ktype
代表这个kobject的类型属性,它是struct kobj_type
类型,该结构体定义在include/linux/kobject.h文件中,如下
struct kobj_type {
void (*release)(struct kobject *kobj);//release函数用于在释放kobject对象时进行一些清理工作
const struct sysfs_ops *sysfs_ops; //struct sysfs_ops类型的指针
struct attribute **default_attrs;//struct attribute数组,其中保存了kobject的默认attribute
const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
const void *(*namespace)(struct kobject *kobj);
};
其中 sysfs_ops
是struct sysfs_ops
类型的指针,该结构体定义在include/linux/sysfs.h文件中,其定义如下:
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
sysfs_ops
定义了对struct attribute
进行操作的函数,其中,show用于将要显示的内容填充到第三个参数指定的内存空间中,并最终显示给用户空间。store用于设置attribute内容。
而 kobj_type
结构体中的default_attrs
是一个struct attribute
数组,其中保存了kobject的默认attribute。struct attribute
定义在include/linux/sysfs.h文件中,其内容如下:
struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:1;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};
在将kobject加入到sysfs文件系统后,在该kobject对应的目录下,会创建kobject的属性文件,default_attrs
数组中指定了几个属性,就会创建几个对应的属性文件,那么sysfs_ops->show
在读属性文件时被调用,sysfs_ops->store
在写属性文件时被调用。
未完待续。。。。。。