#include <linux/kobject.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/module.h>
#include <linux/init.h>
/*
* This module shows how to create a simple subdirectory in sysfs called
* /sys/kernel/kobject-example In that directory, 3 files are created:
* "foo", "baz", and "bar". If an integer is written to these files, it can be
* later read out of it.
*/
static int foo;
static int baz;
static int bar;
/*
* The "foo" file where a static variable is read from and written to.
*/
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", foo);
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%du", &foo);
return count;
}
/* Sysfs attributes cannot be world-writable. */
static struct kobj_attribute foo_attribute =
__ATTR(foo, 0664, foo_show, foo_store);
/*
* More complex function where we determine which variable is being accessed by
* looking at the attribute for the "baz" and "bar" files.
*/
static ssize_t b_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
int var;
if (strcmp(attr->attr.name, "baz") == 0)
var = baz;
else
var = bar;
return sprintf(buf, "%d\n", var);
}
static ssize_t b_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
int var;
sscanf(buf, "%du", &var);
if (strcmp(attr->attr.name, "baz") == 0)
baz = var;
else
bar = var;
return count;
}
static struct kobj_attribute baz_attribute =
__ATTR(baz, 0664, b_show, b_store);
static struct kobj_attribute bar_attribute =
__ATTR(bar, 0664, b_show, b_store);
/*
* Create a group of attributes so that we can create and destroy them all
* at once.
*/
static struct attribute *attrs[] = {
&foo_attribute.attr,
&baz_attribute.attr,
&bar_attribute.attr,
NULL, /* need to NULL terminate the list of attributes */
};
/*
* An unnamed attribute group will put all of the attributes directly in
* the kobject directory. If we specify a name, a subdirectory will be
* created for the attributes with the directory being the name of the
* attribute group.
*/
static struct attribute_group attr_group = {
.attrs = attrs,
};
static struct kobject *example_kobj;
static int __init example_init(void)
{
int retval;
WARN_ON("BBBB");
/*
* Create a simple kobject with the name of "kobject_example",
* located under /sys/kernel/
*
* As this is a simple directory, no uevent will be sent to
* userspace. That is why this function should not be used for
* any type of dynamic kobjects, where the name and number are
* not known ahead of time.
*/
example_kobj = kobject_create_and_add("kobject_example", kernel_kobj);
if (!example_kobj)
return -ENOMEM;
/* Create the files associated with this kobject */
retval = sysfs_create_group(example_kobj, &attr_group);
if (retval)
kobject_put(example_kobj);
return retval;
}
static void __exit example_exit(void)
{
kobject_put(example_kobj);
}
module_init(example_init);
module_exit(example_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Kroah-Hartman <greg@kroah.com>");
这个例子来自于linux kernel 的samples/kboject-example.c
首先这个kobject的例子实现的功能是在/sys/kernel下面创建kobject-example 目录。并且创建3个文件,分别是foo, baz bar , 如果向这些文件中写入数字,便可以读出来,这与我们平时操作sys文件系统来修改内核参数的行文很像,也就是提供了用户层配置内核层的接口
我们来看一下是如何实现的, example_init函数
1 example_kobj = kobject_create_and_add(“kobject_example”, kernel_kobj) 创建并添加到系统中,这个函数隐藏了很多细节,我们先不分析, 函数第一个参数为创建目录的名字,第二个参数为父kobject(其实也对应与父目录,kernel_kobj为/sys/kernel目录)
2 目录创建好了就开始调用sysfs_create_group 创建目录下的属性(keyobject对应文件夹,而属性就对应文件), 我们常见的sys文件系统下驱动的配置文件也是这样的, 举个例子/sys/block/sda目录为一个块设备的sys系统描述, 这个目录为一个kobject,它下面有一些文件如uevent,如size文件用于描述该磁盘的大小,是kobject的一个属性。另外属性也可以进行分组,分组的好处就是一次可以创建多个属性,销毁多个属性,另外逻辑上属性确实也存在分组。
那么如何描述一个属性呢?
sysfs_create_group 描述了一组属性
/*
* An unnamed attribute group will put all of the attributes directly in
* the kobject directory. If we specify a name, a subdirectory will be
* created for the attributes with the directory being the name of the
* attribute group.
*/
static struct attribute_group attr_group = {
.attrs = attrs,
};
struct attribute_group {
const char *name;
umode_t (*is_visible)(struct kobject *,
struct attribute *, int);
struct attribute **attrs;
struct bin_attribute **bin_attrs;
};
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
};
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);
};
#define __ATTR(_name, _mode, _show, _store) { \
.attr = {.name = __stringify(_name), \
.mode = VERIFY_OCTAL_PERMISSIONS(_mode) }, \
.show = _show, \
.store = _store, \
}
注释描述的很清楚, nam表示该组的名字,如果指定了name则会创建name文件夹,下面才是属性文件,is_visible表示该组是否可见,attrs数组用于描述该组下面的属性,bin_attrs则是二进制属性
.
属性用struct attribute描述.name表示属性名字,也就是要创建的文件的名字, mode为文件的读写权限。 kobj_attribute描述一个kobject的属性,相当于继承了attribute,show函数指针用用户读该属性文件的回调操作,需要填充buf缓冲区,将内容传递给用户, store则对应于用户写该属性文件的回调操作,需要读取该buf缓冲区,了解用户进行何种要求
知道了这些我们来看看例子中是如何配置的属性
_ATTR宏是用于填充属性结构体的宏
static struct kobj_attribute baz_attribute =
__ATTR(baz, 0664, b_show, b_store);
static struct kobj_attribute bar_attribute =
__ATTR(bar, 0664, b_show, b_store);
static struct attribute *attrs[] = {
&foo_attribute.attr,
&baz_attribute.attr,
&bar_attribute.attr,
NULL, /* need to NULL terminate the list of attributes */
};
/* Sysfs attributes cannot be world-writable. */
static struct kobj_attribute foo_attribute =
__ATTR(foo, 0664, foo_show, foo_store);
static struct attribute_group attr_group = {
.attrs = attrs,
};
我们只拿foo_attribute为例看它如何实现store和show方法
static ssize_t foo_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
return sprintf(buf, "%d\n", foo);
}
static ssize_t foo_store(struct kobject *kobj, struct kobj_attribute *attr,
const char *buf, size_t count)
{
sscanf(buf, "%du", &foo);
return count;
}
其实很简单,就是修改和读取foo变量。
下面我们再来看看系统是如何调用到kobject属性的show方法和store方法,我们打印了它的调用栈,如下
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
[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 ]---
从调用栈来看二者都是从sysfs的接口调用过来的, 分别的sysfs_kf_write和sysfs_kf_seq_show函数。
然后调用kobj_attr_store 或者kobj_attr_show 函数。再调用我们的show和store方法。
我们来看看kobj_attr_store 和 kobj_attr_show 方法
/* 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,
};
struct sysfs_ops {
ssize_t (*show)(struct kobject *, struct attribute *, char *);
ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
};
其实就是调用kobj_sysfs_ops结构下的对应方法。 kobj_sysfs_ops是何时注册到回调体系里的?
这要回到我们一开始的kobject_create_and_add函数
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;
}
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;
}
static struct kobj_type dynamic_kobj_ktype = {
.release = dynamic_kobj_release,
.sysfs_ops = &kobj_sysfs_ops,
};
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);
};
static void dynamic_kobj_release(struct kobject *kobj)
{
pr_debug("kobject: (%p): %s\n", kobj, __func__);
kfree(kobj);
}
kobject_create_and_add 函数很简单,就是为kobj分配内存,然后调用kobject_init方法初始化,初始化传递了一个kobj_type类型的参数,这个参数内嵌了sysfs_ops结构体,也就是操作sysfs要调用的方法。 另外kobj_type中的release回调函数在kobject引用计数为0的时候释放kobject占用的内存。
我们再来看一下kobj_attr_show方法,这个方法里面直接就带有attr参数,sysfs是如何找到该结构体对应的实例的呢
可以看看sysfs_create_group函数的实现
最后再来阅读下kobject的内核文档吧,附中文版
https://blog.csdn.net/qb_2008/article/details/6842646