kobject学习

#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

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值