Linux设备驱动模型-Ktype

前言

在之前创建的object的时候,使用的是kobject_create_and_add函数。而此函数中创建的object使用的是默认的ktype(dynamic_kobj_ktype), 如果想指定ktype的话就需要使用kobject_init_and_add函数来创建object。那ktype是具体的作用是什么? ktype其实就是kobject的属性的操作集合,因为某些模块的操作集合相同,所以就将ktype单独抽象出来,这样就实现了代码复用。

数据结构

内核使用kobj_type定义一个ktype结构
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:        通过该回调,做最后的收尾工作,比如释放掉申请的内存等。
sysfs_ops:    该kobject的属性的操作函数,也就是show和store函数,直接可以通过shell命令操作。
struct sysfs_ops {
	ssize_t	(*show)(struct kobject *, struct attribute *, char *);
	ssize_t	(*store)(struct kobject *, struct attribute *, const char *, size_t);
};
defalut_attrs:   代表该kobject所对应的属性,而这些属性最终可以通过sysfs_ops操作。
struct attribute {
	const char		*name;
	umode_t			mode;
};
name就代表该属性的名字,mode代表该属性的权限。可读, 可写, 读写。
namespace/child_ns_type:  是代表namespace的相关,不做过多介绍。

示例

修改上次的示例,增加ktype。
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/kobject.h>
#include <linux/sysfs.h>
#include <linux/init.h>
#include <linux/file.h>
#include <linux/fs.h>

static struct kobject kobj;
static struct kobject* pkobj = NULL;

static void test_kobj_release(struct kobject *kobj)
{
	printk(KERN_EMERG "kobject: test_kobj_release!\n");
}

static ssize_t test_attr_show(struct kobject *kobj, struct attribute *attr, char *page)
{
	printk(KERN_EMERG "kobject: test_attr_show!\n");
	return 0;
}

static ssize_t test_attr_store(struct kobject *kobj, struct attribute *attr, const char *page, size_t length)
{
	printk(KERN_EMERG "kobject: test_attr_shore!\n");
	return 123;
}

static const struct sysfs_ops test_sysfs_ops = {
	.show  = test_attr_show,
	.store = test_attr_store,
};

static struct kobj_type test_ktype = {
	.sysfs_ops = &test_sysfs_ops,
	.release = test_kobj_release,
};


static struct attribute test_attr ={
	.name = "test",
	.mode = S_IWUSR | S_IRUGO,
};

static int kobject_test_init(void)
{
	int ret = 0;

	printk(KERN_EMERG "kobject: kobject_test_init!\n");
	pkobj = kobject_create_and_add("123_test", NULL);
	ret = kobject_init_and_add(&kobj, &test_ktype, pkobj, "%s", "456_test");

	ret = sysfs_create_file(&kobj, &test_attr);
	return ret;
}

static void kobject_test_exit(void)
{
	printk(KERN_EMERG "kobject: kobject_test_exit!\n");
	sysfs_remove_file(&kobj, &test_attr);
	kobject_del(&kobj);
	kobject_del(pkobj);
}

module_init(kobject_test_init);
module_exit(kobject_test_exit);
MODULE_LICENSE("GPL v2");
上述是个最简单的测试,会在sys下生成这样的文件:  /sys/123_test/456_test/test
测试结果如下:
root@test:/sys/123_test/456_test # ls -l 
-rw-r--r-- root     root         4096 2012-01-01 09:01 test
可以看到该文件是可读可写的。这样的test文件来自于上述属性的设置。
执行的流程如下
root@test:/# insmod object.ko
root@test:/sys/123_test/456_test # cat test
root@test:/sys/123_test/456_test # echo 123 > test
root@test:/sys/123_test/456_test # rmmod object.ko
root@test:/sys/123_test/456_test # dmesg | grep "kobject"
测试结果如下:
[   74.484138] c4 kobject: kobject_test_init!
[  101.197689] c0 kobject: test_attr_show!
[  111.062197] c0 kobject: test_attr_shore!
[  129.452907] c7 kobject: kobject_test_exit!

分析处理流程

先分析sysfs_create_file函数的处理流程
sysfs_create_file
    sysfs_create_file_ns
         sysfs_add_file_mode_ns
int sysfs_add_file_mode_ns(struct kernfs_node *parent,
			   const struct attribute *attr, bool is_bin,
			   umode_t mode, const void *ns)
{
	struct lock_class_key *key = NULL;
	const struct kernfs_ops *ops;
	struct kernfs_node *kn;
	loff_t size;

	if (!is_bin) {
		struct kobject *kobj = parent->priv;
		const struct sysfs_ops *sysfs_ops = kobj->ktype->sysfs_ops;               //不是bin文件属性的时候,设置Ktype中的sysfs_ops

		/* every kobject with an attribute needs a ktype assigned */
		if (WARN(!sysfs_ops, KERN_ERR
			 "missing sysfs attribute operations for kobject: %s\n",
			 kobject_name(kobj)))
			return -EINVAL;

		if (sysfs_ops->show && sysfs_ops->store)                  //show和store函数都存在的话,也就是可读可写,就会赋值ops。
			ops = &sysfs_file_kfops_rw;
		else if (sysfs_ops->show)                                 //只读
			ops = &sysfs_file_kfops_ro;
		else if (sysfs_ops->store)                               //只写
			ops = &sysfs_file_kfops_wo;
		else
			ops = &sysfs_file_kfops_empty;                    //空的

		size = PAGE_SIZE;
	} else {
                  .......
	}

	kn = __kernfs_create_file(parent, attr->name, mode, size, ops,      //重要的参数,ops和attr
				  (void *)attr, ns, true, key);
	if (IS_ERR(kn)) {
		if (PTR_ERR(kn) == -EEXIST)
			sysfs_warn_dup(parent, attr->name);
		return PTR_ERR(kn);
	}
	return 0;
}
接下来分析__kernfs_create_file函数。记得传入得两个重要的参数。
struct kernfs_node *__kernfs_create_file(struct kernfs_node *parent,
					 const char *name,
					 umode_t mode, loff_t size,
					 const struct kernfs_ops *ops,
					 void *priv, const void *ns,
					 bool name_is_static,
					 struct lock_class_key *key)
{
	struct kernfs_node *kn;
	unsigned flags;
	int rc;

	flags = KERNFS_FILE;
	if (name_is_static)
		flags |= KERNFS_STATIC_NAME;

	kn = kernfs_new_node(parent, name, (mode & S_IALLUGO) | S_IFREG, flags);
	if (!kn)
		return ERR_PTR(-ENOMEM);

	kn->attr.ops = ops;             //设置ops到kn->attr.ops中
	kn->attr.size = size;
	kn->ns = ns;
	kn->priv = priv;
        .......
}
至此,这是注册一个sys文件的准备工作。这时候上层就如果使用到此文件,首先肯定是open的操作,会通过系统调用进入到内核的sys_open函数中,经过一系列的操作最后进入到kernfs_fop_open函数中,关于如何执行到此函数,暂且不分析。
static int kernfs_fop_open(struct inode *inode, struct file *file)
{
	struct kernfs_node *kn = file->f_path.dentry->d_fsdata;
	struct kernfs_root *root = kernfs_root(kn);
	const struct kernfs_ops *ops;
	struct kernfs_open_file *of;
	bool has_read, has_write, has_mmap;
	int error = -EACCES;

	if (!kernfs_get_active(kn))
		return -ENODEV;

	ops = kernfs_ops(kn);                            //得到注册时候的ops

	has_read = ops->seq_show || ops->read || ops->mmap;
	has_write = ops->write || ops->mmap;
	has_mmap = ops->mmap;

	of->kn = kn;
	of->file = file;

	/*
	 * Write path needs to atomic_write_len outside active reference.
	 * Cache it in open_file.  See kernfs_fop_write() for details.
	 */
	of->atomic_write_len = ops->atomic_write_len;

	/*
	 * Always instantiate seq_file even if read access doesn't use
	 * seq_file or is not requested.  This unifies private data access
	 * and readable regular files are the vast majority anyway.
	 */
	if (ops->seq_show)                                                //如果ops中存在seq_show就会调用seq_open函数。
		error = seq_open(file, &kernfs_seq_ops);
	else
		error = seq_open(file, NULL);
	if (error)
		goto err_free;

	((struct seq_file *)file->private_data)->private = of;

	/* seq_file clears PWRITE unconditionally, restore it if WRITE */
	if (file->f_mode & FMODE_WRITE)
		file->f_mode |= FMODE_PWRITE;

	/* make sure we have open node struct */
	error = kernfs_get_open_node(kn, of);
	if (error)
		goto err_close;

	/* open succeeded, put active references */
	kernfs_put_active(kn);
	return 0;
}
上述的代码会删除一些不相关的操作,依次来分析重点函数。
static const struct kernfs_ops *kernfs_ops(struct kernfs_node *kn)
{
	if (kn->flags & KERNFS_LOCKDEP)
		lockdep_assert_held(kn);
	return kn->attr.ops;
可以看到返回了在注册时候设置ops,此ops也就是kernfs_ops。

在seq_open中会设置序列文件的ops为kernfs_seq_ops结构,当再次read文件的时候,会调用到kernfs_seq_ops中的show函数。
static int kernfs_seq_show(struct seq_file *sf, void *v)
{
	struct kernfs_open_file *of = sf->private;

	of->event = atomic_read(&of->kn->attr.open->event);

	return of->kn->attr.ops->seq_show(sf, v);
}
经过一系列指针操作,最后调用到kernfs_ops中的seq_show函数,也就是注册时候的sysfs_file_kfops_rw。
static const struct kernfs_ops sysfs_file_kfops_rw = {
	.seq_show	= sysfs_kf_seq_show,
	.write		= sysfs_kf_write,
};
接着来看下seq_show函数
static int sysfs_kf_seq_show(struct seq_file *sf, void *v)
{
	struct kernfs_open_file *of = sf->private;
	struct kobject *kobj = of->kn->parent->priv;
	const struct sysfs_ops *ops = sysfs_file_ops(of->kn);         -------------------------A
	ssize_t count;
	char *buf;

	/* acquire buffer and ensure that it's >= PAGE_SIZE and clear */
	count = seq_get_buf(sf, &buf);
	if (count < PAGE_SIZE) {
		seq_commit(sf, -1);
		return 0;
	}
	memset(buf, 0, PAGE_SIZE);

	/*
	 * Invoke show().  Control may reach here via seq file lseek even
	 * if @ops->show() isn't implemented.
	 */
	if (ops->show) {
		count = ops->show(kobj, of->kn->priv, buf);    -----------调用到sysfs_show函数,也就是例子中的test_attr_show函数
		if (count < 0)
			return count;
	}

	seq_commit(sf, count);
	return 0;
}
A:  获取ktype中的sysfs_ops
static const struct sysfs_ops *sysfs_file_ops(struct kernfs_node *kn)
{
	struct kobject *kobj = kn->parent->priv;

	if (kn->flags & KERNFS_LOCKDEP)
		lockdep_assert_held(kn);
	return kobj->ktype ? kobj->ktype->sysfs_ops : NULL;
}
通过判断kobj的ktype时候存在,如果存在返回sysfs_ops结构。最终调用到show函数中。
当然了store函数同理,这里就不在详细分析了。

补充

刚才在测试中忽略了release函数的调用,先来看看release函数在什么条件下会调用到。
kobject_put
     kobject_release
          kobject_cleanup
                
/*
 * 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) {                                   //如果存在release就调用到release函数。在我们测测试case中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(name);
	}
}
所以基于上述的执行路径,在驱动的exit函数中调用kobject_put函数。
static void kobject_test_exit(void)
{
	printk(KERN_EMERG "kobject: kobject_test_exit!\n");
	sysfs_remove_file(&kobj, &test_attr);
	kobject_put(&kobj);
	kobject_del(&kobj);
	kobject_del(pkobj);
}
插入模块,重新测试,就会调到release函数。
root@test:/data # dmesg | grep "kobject"                            
[ 4136.988959] c5 kobject: kobject_test_init!
[ 4160.808964] c6 kobject: kobject_test_exit!
[ 4160.813139] c6 kobject: test_kobj_release!
当然了release函数中一般会做一些内存释放的操作,这里只是打印信息而已。


至此分析完了整个执行流程。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值