1、前言

Linux内核中有大量的驱动,而这些驱动往往具有类似的结构,根据面向对象的思想,可以将共同的部分提取为父类,而这个父类就是kobject,kobject结构体中包含了大量设备的必须信息,而三大类设备驱动都需要包含这个kobject结构,运用面向对象的思想来看问题,也就是继承来自kobject,一个kobject对象往往就对应sysfs中的一个目录,kobject是组成设备模型的基本结构,kobject需要处理的基本任务有如下:

(1)对象的引用计数

当一个内核对象被创建时,不可能知道对象的存活时间,跟踪该对象的的生命周期的一个方法就是使用引用计数,当内核中没有代码持有该对象的引用时,说明该对象可以被销毁了;

(2)sysfs表述

一个kobject对象往往对应sysfs中的一个目录,kobject被用来与内核交互并创建它的sysfs可见表述;

(3)数据结构关联

从整体上看,设备模型是一个友好而复杂的数据结构,通过kobject对象实现了大量的连接而构成了一个多层次的体系结构;

(4)热拔插事件处理

当系统中的硬件被热插拔时,在kobject子系统的控制下,将产生事件以通知用户空间。

2、kobject以及相关结构体

Linux内核源码中对kobject结构体的定义在include/linux/kobject.h文件中,实现在lib/kobjet.c,struct kobject结构体的定义如下所示:

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:;
unsigned int state_in_sysfs:;
unsigned int state_add_uevent_sent:;
unsigned int state_remove_uevent_sent:;
unsigned int uevent_suppress:;
};

结构体常用成员解释:

name:表示kobject对象的名字,对应sysfs下的一个目录;

entry:在kobject中嵌入双向链表list_head结构;

parent:指向当前kobject父对象的指针;

kset:表示当前kobject对象所属的集合;

ktype:表示当前kobject对象所属类型;

sd:用于VFS文件系统的目录项,是设备与文件之间的桥梁,sysfs中的符号链接就是通过kernfs_node内的联合体实现的;

kref:是对kobject的引用计数,当引用计数为0时,就回调之前注册的release函数释放该对象;

state_initialized:1:初始化的标志位,在对象被初始化时置位,用于表示对象已被初始化;

state_in_sysfs:1:表示kobject对象在sysfs中的状态,在对应目录中被创建则置1,否则为0;

state_add_uevent_sent:1:添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备;

state_remove_uevent_sent:1:删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备。

struct kobj_type定义了kobject的类型,该结构体既有操作的函数,也有默认的公共属性,该结构体定义如下:

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);
void (*get_ownership)(struct kobject *kobj, kuid_t *uid, kgid_t *gid);
};

结构体常用成员解释:

release:函数指针,当引用计数为0时,在kobject释放时调用;

sysfs_ops:定义了读写属性文件时调用的函数;

default_attrs:定义了这类kobject默认属性,指向attribute数组的指针。

struct sysfs_ops结构体是用于实现属性的的函数操作集,其定义如下所示:

struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};

show:函数用于读取一个属性到用户空间,函数的第一个参数是要读取的kobject指针,它对应要读的目录,第二个参数是要读的属性,第三个参数存放读到的属性的缓冲区,当函数调用成功后,返回实际读取的数据长度;

store:函数用于将属性写入到内核,函数第一个参数是与写相关的kobject指针,它对应要写的目录,第二个参数是要写的属性,第三个参数是要写入的数据,第四个参数是要写入的参数长度,这个长度不能超过PAGESIZE个字节大小,只有当拥有属性写权限时,才能调用store()函数。

struct attribute定义了kobject的属性,该结构体定义如下:

struct attribute {
const char *name;
umode_t mode;
#ifdef CONFIG_DEBUG_LOCK_ALLOC
bool ignore_lockdep:;
struct lock_class_key *key;
struct lock_class_key skey;
#endif
};

常用结构体成员解释:

name:属性文件的名称,属性文件将在kobject的sysfs目录中显示;

mode:属性文件的读写权限。

struct kset可以看成是在kobject上的拓展,它包含了一个kobject的链表,可以方便地表示sysfs中目录与子目录的关系,该结构体定义如下:

/**
* 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;
struct kobject kobj;
const struct kset_uevent_ops *uevent_ops;
};

结构体常用成员解释:

list:用来挂在链表上的结构,包含在一个kset的所有kobject构成了一个双向循坏链表,list_head是这个链表的头部,这个链表用来连接第一个和最后一个kobject对象,第一个kobject使用entry连接kset集合以及第二个kobject对象,第二个kobject对象使用entry连接第一个kobject对象和第三个kobject对象,最终形成一个kobject对象的链表;

list_lock:用于迭代kobjects的锁;

kobj:归属于该kset的所有的kobject的共有parent,这个parent就是体现内核设备组织结构的关键,同时,kset的引用计数就是内嵌的kobject对象的引用计数;

uevent_ops:此kset的uevent操作函数集。

struct kobj_attribute是kobject在attribute上的拓展,添加了专门读写kobject_attribute属性的函数,结构体定义如下:

struct kobj_attribute {
struct attribute attr;
ssize_t (*show)(struct kobject *kobj, struct kobj_attribute *attr,
char *buf);
ssize_t (*store)(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count);
};

结构体成员解释:

attr:kobject的公共属性,包括名称和读写权限;

show:函数指针,用于读取属性到用户空间;

store:函数指针,用于用户空间写属性到内核。

注意:无论是kobjec还是kset(说到底是kset内部的kobject),都提供了使用kobj_attribute的快速创建方法。

3、kobject实现操作

在Linux内核中提供了一系列kobject的实现方法,接下来,进行简要分析:

/*
* 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 = ;
int i; if (t && t->default_attrs) {
for (i = ; (attr = t->default_attrs[i]) != NULL; i++) {
error = sysfs_create_file(kobj, attr);//在sysfs目录创建属性文件
if (error)
break;
}
}
return error;
} 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));//创建sysfs目录
if (error)
return error; error = populate_dir(kobj);//在sysfs目录中填充属性文件
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);//引用计数加1 /*
* 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 ;
}

上面两个函数中,create_dir()用于在sysfs中创建kobj相应的目录,populate_dir()创建kobj中默认属性对应的文件,create_dir()函数就是调用populate_dir()实现的。

static int get_kobj_path_length(struct kobject *kobj)
{
int length = ;
struct kobject *parent = kobj; /* walk up the ancestors until we hit the one pointing to the
* root.
* Add 1 to strlen for leading '/' of each level.
*/
do {
if (kobject_name(parent) == NULL)
return ;
length += strlen(kobject_name(parent)) + ;
parent = parent->parent;
} while (parent);
return length;
} static void fill_kobj_path(struct kobject *kobj, char *path, int length)
{
struct kobject *parent; --length;
for (parent = kobj; parent; parent = parent->parent) {
int cur = strlen(kobject_name(parent));
/* back up enough to print this name with '/' */
length -= cur;
strncpy(path + length, kobject_name(parent), cur);
*(path + --length) = '/';
} pr_debug("kobject: '%s' (%p): %s: path = '%s'\n", kobject_name(kobj),
kobj, __func__, path);
} /**
* kobject_get_path - generate and return the path associated with a given kobj and kset pair.
*
* @kobj: kobject in question, with which to build the path
* @gfp_mask: the allocation type used to allocate the path
*
* The result must be freed by the caller with kfree().
*/
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
{
char *path;
int len; len = get_kobj_path_length(kobj);
if (len == )
return NULL;
path = kzalloc(len, gfp_mask);
if (!path)
return NULL;
fill_kobj_path(kobj, path, len); return path;
}

在上面的三个函数中,前面两个函数是内部函数,get_kobj_path_length()函数用于获得kobj路径名的长度,fill_kobj_path()函数用于将kobj路径名填充到path缓冲区,然后将指针返回,kobject_get_path()函数就是调用这两个函数进行实现的。

/* add the kobject to its kset's list */
static void kobj_kset_join(struct kobject *kobj)
{
if (!kobj->kset)
return; kset_get(kobj->kset);//引用计数加1
spin_lock(&kobj->kset->list_lock);
list_add_tail(&kobj->entry, &kobj->kset->list);//在链表尾部插入节点
spin_unlock(&kobj->kset->list_lock);
} /* remove the kobject from its kset's list */
static void kobj_kset_leave(struct kobject *kobj)
{
if (!kobj->kset)
return; spin_lock(&kobj->kset->list_lock);
list_del_init(&kobj->entry);//链表删除节点
spin_unlock(&kobj->kset->list_lock);
kset_put(kobj->kset);//引用计数减1
}

上面两个函数的功能是相对的,kobj_kset_join()用于将kobj加入到kobj->kset的链表中,kobj_kset_leave()用于将kobj从kobj->kset的链表中删除。

static void kobject_init_internal(struct kobject *kobj)
{
if (!kobj)
return;
kref_init(&kobj->kref);//引用计数初始化
INIT_LIST_HEAD(&kobj->entry);//链表初始化
kobj->state_in_sysfs = ;
kobj->state_add_uevent_sent = ;
kobj->state_remove_uevent_sent = ;
kobj->state_initialized = ;
} static int kobject_add_internal(struct kobject *kobj)
{
int error = ;
struct kobject *parent; if (!kobj)
return -ENOENT; if (!kobj->name || !kobj->name[]) {
WARN(, "kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
return -EINVAL;
} parent = kobject_get(kobj->parent);//父kobject引用计数加1 /* join kset if set, use it as parent if we do not already have one */
if (kobj->kset) {
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;//设置kobject的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);//在sysfs中创建目录
if (error) {
kobj_kset_leave(kobj);
kobject_put(parent);
kobj->parent = NULL; /* be noisy on error issues */
if (error == -EEXIST)
pr_err("%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
pr_err("%s failed for %s (error: %d parent: %s)\n",
__func__, kobject_name(kobj), error,
parent ? kobject_name(parent) : "'none'");
} else
kobj->state_in_sysfs = ; return error;
}

kobject_init_internal()函数用来初始化kobj,kobject_add_internal()函数用来将kobj加入已有的结构中去,这两个函数有密切的联系,在kobject结构体中有很多变量,但是重要的只有两个,一个是kset,一个是parent,这两个变量表示了kobject在整个体系中的位置,决不能自行决定,需要外部参与设置,kobject的创建过程分为init和add两个阶段。kobject_init_internal()把一些结构体变量进行初始化后,等外界设置了parent和kset后,再调用kobject_add_internal()把kobject添加到适当的位置,并创建相应的sysfs目录及文件。

/**
* kobject_set_name_vargs - Set the name of an kobject
* @kobj: struct kobject to set the name of
* @fmt: format string used to build the name
* @vargs: vargs to format the string.
*/
int kobject_set_name_vargs(struct kobject *kobj, const char *fmt,
va_list vargs)
{
const char *s; if (kobj->name && !fmt)
return ; s = kvasprintf_const(GFP_KERNEL, fmt, vargs);
if (!s)
return -ENOMEM; /*
* ewww... some of these buggers have '/' in the name ... If
* that's the case, we need to make sure we have an actual
* allocated copy to modify, since kvasprintf_const may have
* returned something from .rodata.
*/
if (strchr(s, '/')) {
char *t; t = kstrdup(s, GFP_KERNEL);
kfree_const(s);
if (!t)
return -ENOMEM;
strreplace(t, '/', '!');
s = t;
}
kfree_const(kobj->name);
kobj->name = s; return ;
} /**
* kobject_set_name - Set the name of a kobject
* @kobj: struct kobject to set the name of
* @fmt: format string used to build the name
*
* This sets the name of the kobject. If you have already added the
* kobject to the system, you must call kobject_rename() in order to
* change the name of the kobject.
*/
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
{
va_list vargs;
int retval; va_start(vargs, fmt);
retval = kobject_set_name_vargs(kobj, fmt, vargs);
va_end(vargs); return retval;
}

kobject_set_name()函数的作用是设置kobj的名称,它通过调用kobject_set_name_vargs()来实现,需要注意的时,kobject_set_name()仅限于kobject添加到体系之前使用,因为该函数只是修改了名字,并未通知到用户空间。

/**
* 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) {
err_str = "must have a ktype to be initialized properly!\n";
goto error;
}
if (kobj->state_initialized) {
/* 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);
kobj->ktype = ktype;
return; error:
printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
dump_stack();
}

kobject_init()函数就是调用了kobject_init_internal()函数进行一些结构体变量初始化,然后又设置了ktype结构体成员,ktype的作用是管理一些默认属性。

static 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);
if (retval) {
printk(KERN_ERR "kobject: can not set name properly!\n");
return retval;
}
kobj->parent = parent;
return kobject_add_internal(kobj);
} /**
* kobject_add - the main kobject add function
* @kobj: the kobject to add
* @parent: pointer to the parent of the kobject.
* @fmt: format to name the kobject with.
*
* The kobject name is set and added to the kobject hierarchy in this
* function.
*
* If @parent is set, then the parent of the @kobj will be set to it.
* If @parent is NULL, then the parent of the @kobj will be set to the
* kobject associated with the kset assigned to this kobject. If no kset
* is assigned to the kobject, then the kobject will be located in the
* root of the sysfs tree.
*
* If this function returns an error, kobject_put() must be called to
* properly clean up the memory associated with the object.
* Under no instance should the kobject that is passed to this function
* be directly freed with a call to kfree(), that can leak memory.
*
* Note, no "add" uevent will be created with this call, the caller should set
* up all of the necessary sysfs files for the object and then call
* kobject_uevent() with the UEVENT_ADD parameter to ensure that
* userspace is properly notified of this kobject's creation.
*/
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
{
va_list args;
int retval; if (!kobj)
return -EINVAL; if (!kobj->state_initialized) {
printk(KERN_ERR "kobject '%s' (%p): tried to add an "
"uninitialized object, something is seriously wrong.\n",
kobject_name(kobj), kobj);
dump_stack();
return -EINVAL;
}
va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args); return retval;
}

kobject_add()函数用于将kobj添加到体系中去,但是该函数还有一个附加的功能,设置kobj的名字,parent作为参数被传进来。

/**
* 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); va_start(args, fmt);
retval = kobject_add_varg(kobj, parent, fmt, args);
va_end(args); return retval;
}

kobject_init_and_add()其实是kobjec_init()和kobject_add()的合并,将kobject初始化后并添加到kobject的层次结构中去。

/**
* kobject_rename - change the name of an object
* @kobj: object in question.
* @new_name: object's new name
*
* It is the responsibility of the caller to provide mutual
* exclusion between two different calls of kobject_rename
* on the same kobject and to ensure that new_name is valid and
* won't conflict with other kobjects.
*/
int kobject_rename(struct kobject *kobj, const char *new_name)
{
int error = ;
const char *devpath = NULL;
const char *dup_name = NULL, *name;
char *devpath_string = NULL;
char *envp[]; kobj = kobject_get(kobj);
if (!kobj)
return -EINVAL;
if (!kobj->parent)
return -EINVAL; devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
goto out;
}
devpath_string = kmalloc(strlen(devpath) + , GFP_KERNEL);
if (!devpath_string) {
error = -ENOMEM;
goto out;
}
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[] = devpath_string;
envp[] = NULL; name = dup_name = kstrdup_const(new_name, GFP_KERNEL);
if (!name) {
error = -ENOMEM;
goto out;
} error = sysfs_rename_dir_ns(kobj, new_name, kobject_namespace(kobj));
if (error)
goto out; /* Install the new kobject name */
dup_name = kobj->name;
kobj->name = name; /* This function is mostly/only used for network interface.
* Some hotplug package track interfaces by their name and
* therefore want to know when the name is changed by the user. */
kobject_uevent_env(kobj, KOBJ_MOVE, envp); out:
kfree_const(dup_name);
kfree(devpath_string);
kfree(devpath);
kobject_put(kobj); return error;
}

kobject_rename()函数实现的功能为,当kobj已经添加到系统后,可以使用此函数调用实现更改kobject的名字,它除了能完成kobject_set_name()的功能以外,还向用户空间通知这一消息。

/**
* kobject_move - move object to another parent
* @kobj: object in question.
* @new_parent: object's new parent (can be NULL)
*/
int kobject_move(struct kobject *kobj, struct kobject *new_parent)
{
int error;
struct kobject *old_parent;
const char *devpath = NULL;
char *devpath_string = NULL;
char *envp[]; kobj = kobject_get(kobj);
if (!kobj)
return -EINVAL;
new_parent = kobject_get(new_parent);
if (!new_parent) {
if (kobj->kset)
new_parent = kobject_get(&kobj->kset->kobj);
} /* old object path */
devpath = kobject_get_path(kobj, GFP_KERNEL);
if (!devpath) {
error = -ENOMEM;
goto out;
}
devpath_string = kmalloc(strlen(devpath) + , GFP_KERNEL);
if (!devpath_string) {
error = -ENOMEM;
goto out;
}
sprintf(devpath_string, "DEVPATH_OLD=%s", devpath);
envp[] = devpath_string;
envp[] = NULL;
error = sysfs_move_dir_ns(kobj, new_parent, kobject_namespace(kobj));
if (error)
goto out;
old_parent = kobj->parent;
kobj->parent = new_parent;
new_parent = NULL;
kobject_put(old_parent);
kobject_uevent_env(kobj, KOBJ_MOVE, envp);
out:
kobject_put(new_parent);
kobject_put(kobj);
kfree(devpath_string);
kfree(devpath);
return error;
}

kobjec_move()函数实现的功能是在kobj添加到系统后,调用此函数能将kobj移动到新的parent下,同时会通知用户空间。

/**
* kobject_del - unlink kobject from hierarchy.
* @kobj: object.
*/
void kobject_del(struct kobject *kobj)
{
struct kernfs_node *sd; if (!kobj)
return; sd = kobj->sd;
sysfs_remove_dir(kobj);
sysfs_put(sd); kobj->state_in_sysfs = ;
kobj_kset_leave(kobj);
kobject_put(kobj->parent);
kobj->parent = NULL;
}

kobject_del()函数用于将kobj从系统中删除,和kobject_add()是相对的操作。

/**
* kobject_get - increment refcount for object.
* @kobj: object.
*/
struct kobject *kobject_get(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized)
WARN(, 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_cleanup - free kobject resources.
* @kobj: object to cleanup
*/
static void kobject_cleanup(struct kobject *kobj)
{
struct kobj_type *t = get_ktype(kobj);
const char *name = kobj->name; pr_debug("kobject: '%s' (%p): %s, parent %p\n",
kobject_name(kobj), kobj, __func__, kobj->parent); if (t && !t->release)
pr_debug("kobject: '%s' (%p): does not have a release() "
"function, it is broken and must be fixed.\n",
kobject_name(kobj), kobj); /* send "remove" if the caller did not do it but sent "add" */
if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
kobject_name(kobj), kobj);
kobject_uevent(kobj, KOBJ_REMOVE);
} /* remove from sysfs if the caller did not do it */
if (kobj->state_in_sysfs) {
pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
kobject_name(kobj), kobj);
kobject_del(kobj);
} if (t && t->release) {
pr_debug("kobject: '%s' (%p): calling ktype release\n",
kobject_name(kobj), kobj);
t->release(kobj);
} /* free name if we allocated it */
if (name) {
pr_debug("kobject: '%s': free name\n", name);
kfree_const(name);
}
} #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
static void kobject_delayed_cleanup(struct work_struct *work)
{
kobject_cleanup(container_of(to_delayed_work(work),
struct kobject, release));
}
#endif static void kobject_release(struct kref *kref)
{
struct kobject *kobj = container_of(kref, struct kobject, kref);
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
unsigned long delay = HZ + HZ * (get_random_int() & 0x3);
pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld)\n",
kobject_name(kobj), kobj, __func__, kobj->parent, delay);
INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup); schedule_delayed_work(&kobj->release, delay);
#else
kobject_cleanup(kobj);
#endif
} /**
* kobject_put - decrement refcount for object.
* @kobj: object.
*
* Decrement the refcount, and if 0, call kobject_cleanup().
*/
void kobject_put(struct kobject *kobj)
{
if (kobj) {
if (!kobj->state_initialized)
WARN(, KERN_WARNING "kobject: '%s' (%p): is not "
"initialized, yet kobject_put() is being "
"called.\n", kobject_name(kobj), kobj);
kref_put(&kobj->kref, kobject_release);
}
}

kobject_get()和kobject_put()函数完成的是kobject的引用计数功能,kobject_put()函数在当引用计数为0时,撤销整个kobject的存在:向用户空间发送REMOVE信息,从sysfs中删除相应的目录,并调用ktype中定义的release函数,并释放name所占用的空间。kobject_cleanup()函数用来释放kobject创建时所分配的资源。

上述提到到API接口,基本上概括了kobject从创建到删除,包括kobject的名字修改、位置修改以及引用计数的变动等功能。但是,kobject的创建仍然比较麻烦,因为ktype需要自己去实现,下面是Linux内核中为kobject提供的一种快速创建的方法:

首先是kobject的属性文件的读写函数定义:

/* default kobject attribute operations */
static ssize_t kobj_attr_show(struct kobject *kobj, struct attribute *attr,
char *buf)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->show)
ret = kattr->show(kobj, kattr, buf);
return ret;
} static ssize_t kobj_attr_store(struct kobject *kobj, struct attribute *attr,
const char *buf, size_t count)
{
struct kobj_attribute *kattr;
ssize_t ret = -EIO; kattr = container_of(attr, struct kobj_attribute, attr);
if (kattr->store)
ret = kattr->store(kobj, kattr, buf, count);
return ret;
} const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};

然后是,kobject的释放函数,以及kobj_type结构体变量的定义,如下:

static void dynamic_kobj_release(struct kobject *kobj)
{
pr_debug("kobject: (%p): %s\n", kobj, __func__);
kfree(kobj);
} static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};

这是Linux内核里面为kobject自身提供的一种kobj_type,名称叫做dynamic_kobj_type,它并没有提供默认的属性,但是提供了release函数和访问属性的方法。

/**
* kobject_create - create a struct kobject dynamically
*
* This function creates a kobject structure dynamically and sets it up
* to be a "dynamic" kobject with a default release function set up.
*
* If the kobject was not able to be created, NULL will be returned.
* The kobject structure returned from here must be cleaned up with a
* call to kobject_put() and not kfree(), as kobject_init() has
* already been called on this structure.
*/
struct kobject *kobject_create(void)
{
struct kobject *kobj; kobj = kzalloc(sizeof(*kobj), GFP_KERNEL);
if (!kobj)
return NULL; kobject_init(kobj, &dynamic_kobj_ktype);
return kobj;
} /**
* kobject_create_and_add - create a struct kobject dynamically and register it with sysfs
*
* @name: the name for the kobject
* @parent: the parent kobject of this kobject, if any.
*
* This function creates a kobject structure dynamically and registers it
* with sysfs. When you are finished with this structure, call
* kobject_put() and the structure will be dynamically freed when
* it is no longer being used.
*
* If the kobject was not able to be created, NULL will be returned.
*/
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
int retval; kobj = kobject_create();
if (!kobj)
return NULL; retval = kobject_add(kobj, parent, "%s", name);
if (retval) {
printk(KERN_WARNING "%s: kobject_add error: %d\n",
__func__, retval);
kobject_put(kobj);
kobj = NULL;
}
return kobj;
}

在kobject_create()和kobject_create_add()函数中,使用了dynamic_kobj_ktype这个结构体变量,这是一种很好的偷懒方式,因为release()函数会释放掉kobj,所以这里的kobj必须是动态创建的,这里的kobject_create()和kobject_init()相对,kobject_create_and_add()和kobject_init_and_add()相对。需要注意的是,这里创建的kobject无法嵌入到其它结构,是独立存在的,所以用到的地方很少。

/**
* kset_init - initialize a kset for use
* @k: kset
*/
void kset_init(struct kset *k)
{
kobject_init_internal(&k->kobj);
INIT_LIST_HEAD(&k->list);
spin_lock_init(&k->list_lock);
}

kset_init()函数用于将kset结构体初始化,和kobject初始化类似。

/**
* kset_register - initialize and add a kset.
* @k: kset.
*/
int kset_register(struct kset *k)
{
int err; if (!k)
return -EINVAL; kset_init(k);
err = kobject_add_internal(&k->kobj);
if (err)
return err;
kobject_uevent(&k->kobj, KOBJ_ADD);
return ;
}

kset_register()函数用来初始化并添加到kset,函数比较简单,它负责将kset中的kobject添加到系统,并向用户空间发布KOBJ_ADD消息,所以在调用之前,需要先设置好k->kobj.name、k->kobj.parent和k->kobj.kset这些成员。

/**
* kset_unregister - remove a kset.
* @k: kset.
*/
void kset_unregister(struct kset *k)
{
if (!k)
return;
kobject_del(&k->kobj);
kobject_put(&k->kobj);
}

kset_unregister()和kset_register()函数功能是相对的,用于移除kset,并减少引用计数。

/**
* kset_find_obj - search for object in kset.
* @kset: kset we're looking in.
* @name: object's name.
*
* Lock kset via @kset->subsys, and iterate over @kset->list,
* looking for a matching kobject. If matching object is found
* take a reference and return the object.
*/
struct kobject *kset_find_obj(struct kset *kset, const char *name)
{
struct kobject *k;
struct kobject *ret = NULL; spin_lock(&kset->list_lock); list_for_each_entry(k, &kset->list, entry) {
if (kobject_name(k) && !strcmp(kobject_name(k), name)) {
ret = kobject_get_unless_zero(k);
break;
}
} spin_unlock(&kset->list_lock);
return ret;
}

kset_find_obj()函数功能为通过从kset的链表中寻找名为name的kobject,实现了链表的遍历。

与kobject类似,kset也提供看一种kobj_type,名称为kset_ktype,实现如下:

static void kset_release(struct kobject *kobj)
{
struct kset *kset = container_of(kobj, struct kset, kobj);
pr_debug("kobject: '%s' (%p): %s\n",
kobject_name(kobj), kobj, __func__);
kfree(kset);
} void kset_get_ownership(struct kobject *kobj, kuid_t *uid, kgid_t *gid)
{
if (kobj->parent)
kobject_get_ownership(kobj->parent, uid, gid);
} static struct kobj_type kset_ktype = {
.sysfs_ops = &kobj_sysfs_ops,
.release = kset_release,
.get_ownership = kset_get_ownership,
}; /**
* kset_create - create a struct kset dynamically
*
* @name: the name for the kset
* @uevent_ops: a struct kset_uevent_ops for the kset
* @parent_kobj: the parent kobject of this kset, if any.
*
* This function creates a kset structure dynamically. This structure can
* then be registered with the system and show up in sysfs with a call to
* kset_register(). When you are finished with this structure, if
* kset_register() has been called, call kset_unregister() and the
* structure will be dynamically freed when it is no longer being used.
*
* If the kset was not able to be created, NULL will be returned.
*/
static struct kset *kset_create(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int retval; kset = kzalloc(sizeof(*kset), GFP_KERNEL);
if (!kset)
return NULL;
retval = kobject_set_name(&kset->kobj, "%s", name);
if (retval) {
kfree(kset);
return NULL;
}
kset->uevent_ops = uevent_ops;
kset->kobj.parent = parent_kobj; /*
* The kobject of this kset will have a type of kset_ktype and belong to
* no kset itself. That way we can properly free it when it is
* finished being used.
*/
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL; return kset;
} /**
* kset_create_and_add - create a struct kset dynamically and add it to sysfs
*
* @name: the name for the kset
* @uevent_ops: a struct kset_uevent_ops for the kset
* @parent_kobj: the parent kobject of this kset, if any.
*
* This function creates a kset structure dynamically and registers it
* with sysfs. When you are finished with this structure, call
* kset_unregister() and the structure will be dynamically freed when it
* is no longer being used.
*
* If the kset was not able to be created, NULL will be returned.
*/
struct kset *kset_create_and_add(const char *name,
const struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
struct kset *kset;
int error; kset = kset_create(name, uevent_ops, parent_kobj);
if (!kset)
return NULL;
error = kset_register(kset);
if (error) {
kfree(kset);
return NULL;
}
return kset;
}

kset_create()和kset_create_and_add()就是使用kset_type来快速创建kset的函数,当需要在sysfs中创建单纯的目录时,可以使用这两个函数进行创建。

static inline struct kset *to_kset(struct kobject *kobj)
{
return kobj ? container_of(kobj, struct kset, kobj) : NULL;
} static inline struct kset *kset_get(struct kset *k)
{
return k ? to_kset(kobject_get(&k->kobj)) : NULL;
} static inline void kset_put(struct kset *k)
{
kobject_put(&k->kobj);
} static inline struct kobj_type *get_ktype(struct kobject *kobj)
{
return kobj->ktype;
}

上面这些函数是在kobject.h文件里面的内联函数,to_kset()函数通过kobj的指针获取kset结构体的首地址,kset_get()和kset_put()为引用计数操作,get_ktype()函数用于获取kobject的ktype类型。

4、 kobject的层次结构

内核用kobject结构将各个对象连接起来组成一个分层的结构体系,从而与模型化的子系统相匹配。kobject结构体中的parent指针成员、kset指针成员和链表list_head实现了kobject的层次结构,parent指针最重要的用途是在sysfs分层结构中定位对象,下图是一个简单的kobject、kset的分层结构图:

5、小节

本文主要对kobject结构体以及相关的结构体以及Linux内核提供的相关API做了简要介绍,kobject结构体和设备驱动模型和sysfs的实现密切相关,必须深刻理解。

参考:

https://blog.csdn.net/yuanmengliang/article/details/52700529

https://blog.csdn.net/qb_2008/article/details/6846779

https://www.cnblogs.com/xiaojiang1025/p/6193959.html

《LINUX设备驱动程序(第三版)》

Linux内核kobject结构体分析的更多相关文章

  1. Linux内核device结构体分析

    1.前言 Linux内核中的设备驱动模型,是建立在sysfs设备文件系统和kobject上的,由总线(bus).设备(device).驱动(driver)和类(class)所组成的关系结构,在底层,L ...

  2. Kobject结构体分析

    kobject是组成设备device.驱动driver.总线bus.class的基本结构.如果把前者看成基类,则后者均为它的派生产物.device.driver.bus.class构成了设备模型,而k ...

  3. Linux中ifreq 结构体分析和使用 及其在项目中的简单应用

    [基础知识说明] 结构原型: /* * Interface request structure used for socket * ioctl's.  All interface ioctl's mu ...

  4. Linux中ifreq 结构体分析和使用

    结构原型: struct ifreq{#define IFHWADDRLEN 6 union {  char ifrn_name[IFNAMSIZ];   } ifr_ifrn;  union {   ...

  5. Linux内核--网络栈实现分析(七)--数据包的传递过程(下)

    本文分析基于Linux Kernel 1.2.13 原创作品,转载请标明http://blog.csdn.net/yming0221/article/details/7545855 更多请查看专栏,地 ...

  6. linux内核SPI总线驱动分析(一)(转)

    linux内核SPI总线驱动分析(一)(转) 下面有两个大的模块: 一个是SPI总线驱动的分析            (研究了具体实现的过程) 另一个是SPI总线驱动的编写(不用研究具体的实现过程) ...

  7. Linux内核--网络栈实现分析(二)--数据包的传递过程--转

    转载地址http://blog.csdn.net/yming0221/article/details/7492423 作者:闫明 本文分析基于Linux Kernel 1.2.13 注:标题中的”(上 ...

  8. linux内核中链表代码分析---list.h头文件分析(一)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637596.html linux内核中链表代码分析---list.h头文件分析(一) 16年2月27日17 ...

  9. linux内核中链表代码分析---list.h头文件分析(二)【转】

    转自:http://blog.chinaunix.net/uid-30254565-id-5637598.html linux内核中链表代码分析---list.h头文件分析(二) 16年2月28日16 ...

随机推荐

  1. .Net IOC框架入门之——Unity

    一.概述 IOC:英文全称:Inversion of Control,中文名称:控制反转,它还有个名字叫依赖注入(Dependency Injection). 作用:将各层的对象以松耦合的方式组织在一 ...

  2. Java虚拟机内存区域详解

    JVM 运行时的数据区域 首先获取一个直观的认识: 总共也就这么 5 个区(直接内存不属于 JVM 运行时数据区的一部分),除了程序计数器其他的地方都有可能出现 OOM (OutOfMemoryErr ...

  3. 架构师小跟班:如何高效又安全的清理Linux服务器上的缓存?

    操作服务器上的生产环境,一定要慎之又慎,安全第一,优化第二! 一些基本原理 说到清理内存,那么不得不提到/proc这一个虚拟文件系统,这里面的数据和文件都是内存中的实时数据,很多参数的获取都可以从下面 ...

  4. web-api POST body object always null

      If the any of values of the request's JSON object are not the same type as expected by the service ...

  5. iOS相关

    1. fastlane a collection of tools that help you automate building and releasing iOS and Android apps ...

  6. windows环境下基于nginx搭建rtmp服务器

    基于nginx搭建rtmp服务器需要引入rtmp模块,引入之后需重新编译nginx linux环境几个命令行就能实现编译,笔者未尝试,网上有很多教程. windows环境还需要安装一系列的编译环境,例 ...

  7. leetcode 学习心得 (4)

    645. Set Mismatch The set S originally contains numbers from 1 to n. But unfortunately, due to the d ...

  8. Spring框架快速入门

    ssm框架是目前最流行的框架之一.他包括Spring,springMVC,mybatis.今天来简单介绍一下spring. 1.spring是什么? 答:spring是一个轻量级的框架. 2.两大核心 ...

  9. rocketmq 两主两从异步集群搭建

    1.安装JDK 需要先卸载系统默认的OPENJDK,安装 JDK1.8 64位的版本. 卸载open-jdk rpm -qa|grep java 查到open jdk的安装. 使用命令 rpm -e ...

  10. Python--RE--?

    ?在re中默认匹配前一个字符0次或者1次 比如: aal? 默认匹配aal,或者aa    即整体匹配前一个字符串,但是可以舍弃最近的一个字符或者不舍弃 re模块 常用正则表达式符号 '.' 默认匹配 ...