1 概述
要说kobject不得不说sysfs
sysfs 是一个最初基于 ramfs 且位于内存的文件系统。它提供导出内核 数据结构及其属性,以及它们之间的关联到用户空间的方法。
sysfs 始终与 kobject 的底层结构紧密相关。
任何 kobject 在系统中注册,就会有一个目录在 sysfs 中被创建。这个目录是作为该 kobject 的父对象所在目录的子目录创建的,以准确地传递内核的对象层次到用户空间。sysfs 中的顶层目录代表着内核对象层次的共同祖先;例如:某些对象属于某个子系统。
kobject 的属性可在文件系统中以普通文件的形式导出。Sysfs 为属性定义了面向文件 I/O 操作的方法,以提供对内核属性的读写。
上面这段话是从内核文档Documentation/zh_CN/filesystems/sysfs.txt 中摘抄的, 主要说明了kobject的作用, 对于注册kobject其实就是在sysfs中创建目录。 在目录中添加属性则用于在sysfs中创建普通文件,读写这些属性文件将会调用内核中为该属性设置的读写回调函数. 所以属性文件用于内核空间和用户空间通信。了解linux系统的人都应该知道sysfs文件一般用于动态配置内核。这段文档没有说明属性组的概念,属性组就是一组属性,这组属性创建的文件在所属属性组目录下。(sysfs的根目录一般问/sys)。
所以kobject系统的拓扑结构大概如下图(注意图中kobject parent 也是kobject,只不过是下面kobject的父kobject):
kobject 和 属性组(attribute_group)都对应目录,属性 (attribute)对应普通文件, kobject下既可以放属性组,也可以放属性, 这就是kobject描述的sysfs文件系统树状结构。
2 使用
有了上述背景知识我们来看下如何添加一个kobject到sysfs,sysfs提供了两个比较常用的接口,用于创建sysfs文件
int sysfs_create_dir_ns(struct kobject *kobj, const char *new_name,
const void *new_ns)
int sysfs_create_file(struct kobject * kobj, const struct attribute * attr)
int sysfs_create_group(struct kobject *kobj, struct attribute_group *grp)
kobject使用sysfs_create_dir_ns 函数创建kobject目录, 使用sysfs_create_file函数创建属性文件,使用sysfs_create_group函数创建属性组目录以及该组下面的属性文件。
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
int kobject_add(struct kobject *kobj, struct kobject *parent,
const char *fmt, ...)
struct kobject *kobject_create(void)
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
int kobject_rename(struct kobject *kobj, const char *new_name)
char *kobject_get_path(struct kobject *kobj, gfp_t gfp_mask)
int kobject_set_name(struct kobject *kobj, const char *fmt, ...)
struct kobject *kobject_get(struct kobject *kobj)
void kobject_put(struct kobject *kobj)
void kobject_del(struct kobject *kobj)
kobject导出的函数主要分为上面五组,每组用空行隔开。
- kobject_init 和 kobject_add 是初初始化kobject和添加kobject到sysfs的步骤。
- kobject_create 提供了一个创建和初始化kobject的封装,并提供了一些kobject创建和初始化的缺省行为。
- kobject_create_and_add 封装了kobject_create 和 kobject_add函数,并提供了一些kobject创建和初始化以及添加kobject到sysfs的缺省行为。
- kobject_init_and_add 封装了kobject_init 和 kobject_add 函数,并提供了一些kobject初始化以及添加kobject到sysfs的缺省行为。
- kobject_rename 用于修改kobject目录名称,kobject_get_path用于获取kobject对应的文件路径,kobject_set_name设置kobject目录名称。
- kobject_get 增加引用计数, kobject_put 减少引用计数,当计数为0的时候会调用kobject_del, 用户也可以调用kobject_del强行删除kobject。
由此可见kobject系统即为我们提供了简单的创建函数,又为我们提供了高度可定制的接口。 最简单的添加kobject到sysfs的方法如下序列:
struct kobject *kobject_create_and_add(const char *name, struct kobject *parent)
比较复杂的序列如下:
kobj =new a kobject
ktype = new a ktype
void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
或者:
kobj = kobject_create(void)
int kobject_add(struct kobject *kobj, struct kobject *parent, const char *fmt, ...)
又或者:
kobj =new a kobject
ktype = new a ktype
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, struct kobject *parent, const char *fmt, ...)
3 源码分析
既然kobject_create_and_add 封装了整个kobject的创建到添加过程, 我们就以他为情景进行分析。
/**
* 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;
}
该函数有两个参数name为kobject的名字,也是要创建到sysfs中文件夹的名字, parent则是父目录对应的kobject,只有确定了父目录才能在文件系统中确定路径。
这个函数很简单,只是调用了kobject_create和kobject_add函数(kobject_create会调用kobject_init)。 在kobject_add函数添加失败的时候会通过kobject_put释放。
kobject_create 为kobject创建和初始化的过程,代码如下:
* 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;
}
函数首先调用kzalloc 申请了kobject的内存,在调用kobject_init 初始化, 注意kobject_init的第二个参数为dynamic_kobj_ktype, 该参数用于设定kobject在sysfs操作中的一些回调函数,和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的state_initialized字段用于记录kobject是否已经初始化,如果二次初始化打印警告日志, 函数只是对异常情况做了一些检查, 真正的初始化工作在kobject_init_internal中,另外 设置kobject->ktype字段。
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);
};
- release 为kobject释放的回调函数,用于清理工作(一般为kfree(kobject))。
- sysfs_ops 则是读写kobject目录下属性文件时的回调。
- default_attrs kobject 默认的属性,默认属性会在添加kobject的时候创建相应的属性文件,额外的属性则需要用户自行调用sysfs_create_file或者 sysfs_create_group添加。
- child_ns_type 命令空间相关。
- namespace 命令空间相关。
下面我们来看下初始化的具体过程kobject_init_internal
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_add 之前我们先来看下 kobject_create 中设置的默认kobj_type, 该值为dynamic_kobj_ktype。
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
缺省的kobject 设置的sysfs读写回调为kobj_sysfs_ops, 释放函数为dynamic_kobj_release, dynamic_kobj_release函数实现的比较简单,就是kfree释放kobject内存,这里就不列出了, kobj_sysfs_ops的实现我们后面再来说明。
前面我们看了kobject创建和初始化的过程,要使kobject发挥作用还需要添加到sysfs中。
/**
* 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,否则打印错误日志并返回。然后调用kobject_add_varg函数来添加kobject到sysfs系统。kobject_add_varg后面的可变参数用于拼装kobject的名称。
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,然后调用kobject_add_internal函数添加kobject到sysfs系统。
static int kobject_add_internal(struct kobject *kobj)
{
int error = 0;
struct kobject *parent;
if (!kobj)
return -ENOENT;
if (!kobj->name || !kobj->name[0]) {
WARN(1, "kobject: (%p): attempted to be registered with empty "
"name!\n", kobj);
return -EINVAL;
}
parent = kobject_get(kobj->parent);
/* 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;
}
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);
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;
return error;
}
涉及kset的部分就不分析了,这个函数时机上最重要的就是调用create_dir(kobj) 来创建sysfs的目录。
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;
}
这部分涉及sysfs和ns,也不深入分析了。
最后我们来看下 kobject属性的数据结构以及定义它的一个宏
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);
};
/**
* Use these macros to make defining attributes easier. See include/linux/device.h
* for examples..
*/
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
可以使用__ATTR宏来定义kobj_attribute,类似如下
__ATTR(foo, 0664, foo_show, foo_store)
这里foo为属性名称,0664为文件权限,foo_show为属性文件读回调,foo_store则为属性文件写回调。
最后我们来看下sysfs如何回调属性文件的读写函数, 还记得dynamic_kobj_ktype中的kobj_sysfs_op,这是一个默认的实现
const struct sysfs_ops kobj_sysfs_ops = {
.show = kobj_attr_show,
.store = kobj_attr_store,
};
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;
}
sysfs在读写属性的时候调用sysfs_ops的show或者store函数,这个函数有一个参数为struct attribute *attr,它实际为 kobj_attribute结构,实际上这个函数的实现只是调用kobj_attribute的show或者store函数。
总的show和store的调用栈如下:
show
vfs_read->__vfs_read->kernfs_fop_read->seq_read->kernfs_seq_show->sysfs_kf_seq_show->kobj_attr_show
[18774.453811] Call Trace:
[18774.453823] [<c16f8aa3>] dump_stack+0x41/0x52
[18774.453830] [<c1062fbe>] warn_slowpath_common+0x8e/0xd0
[18774.453835] [<f9bdd10d>] ? foo_show+0x1d/0x3c [kobject_example]
[18774.453838] [<f9bdd10d>] ? foo_show+0x1d/0x3c [kobject_example]
[18774.453848] [<c1339700>] ? current_is_single_threaded+0xb0/0xb0
[18774.453852] [<c1063022>] warn_slowpath_null+0x22/0x30
[18774.453855] [<f9bdd10d>] foo_show+0x1d/0x3c [kobject_example]
[18774.453863] [<c11982bc>] ? __kmalloc+0xac/0x230
[18774.453866] [<f9bdd0f0>] ? foo_store+0x40/0x40 [kobject_example]
[18774.453872] [<c133970d>] kobj_attr_show+0xd/0x20
[18774.453880] [<c1214a12>] sysfs_kf_seq_show+0xd2/0x170
[18774.453885] [<c12132f4>] kernfs_seq_show+0x24/0x30
[18774.453891] [<c11cd5f6>] seq_read+0xe6/0x360
[18774.453896] [<c1213945>] kernfs_fop_read+0x45/0x60
[18774.453901] [<c1213900>] ? kernfs_file_direct_read+0x110/0x110
[18774.453908] [<c11ae4d6>] __vfs_read+0x26/0x80
[18774.453912] [<c11ae5a7>] vfs_read+0x77/0x120
store
vfs_write->kernfs_fop_write->sysfs_kf_write->kobj_attr_store
[19066.385778] Call Trace:
[19066.385786] [<c16f8aa3>] dump_stack+0x41/0x52
[19066.385791] [<c1062fbe>] warn_slowpath_common+0x8e/0xd0
[19066.385794] [<f9bdd0cd>] ? foo_store+0x1d/0x40 [kobject_example]
[19066.385796] [<f9bdd0cd>] ? foo_store+0x1d/0x40 [kobject_example]
[19066.385798] [<c1063022>] warn_slowpath_null+0x22/0x30
[19066.385801] [<f9bdd0cd>] foo_store+0x1d/0x40 [kobject_example]
[19066.385805] [<c11982bc>] ? __kmalloc+0xac/0x230
[19066.385808] [<f9bdd0b0>] ? b_show+0x50/0x50 [kobject_example]
[19066.385814] [<c133973b>] kobj_attr_store+0x1b/0x30
[19066.385819] [<c121467d>] sysfs_kf_write+0x3d/0x50
[19066.385821] [<c1214640>] ? sysfs_kf_bin_read+0xe0/0xe0
[19066.385824] [<c12137ab>] kernfs_fop_write+0xfb/0x140
[19066.385827] [<c12136b0>] ? kernfs_vma_page_mkwrite+0x80/0x80
[19066.385831] [<c11ae386>] vfs_write+0xa6/0x1d0
[19066.385834] [<c11ae765>] SyS_write+0x55/0xc0
[19066.385837] [<c11c7fc5>] ? __close_fd+0x75/0xa0
[19066.385841] [<c170395f>] sysenter_do_call+0x12/0x12
[19066.385843] ---[ end trace 9ea3de6ceabd4dc1 ]---
最后
到这里kobject就分析的差不多了,不懂了还可以参考kobject内核文档,在内核目录Documentation/kobject.txt, 也可以参考内核提供的例子samples/kobject/kobject-example.c。
预告
后面将分析kset的作用。