Linux设备驱动模型-Ktype
2016-09-23 17:38
477 查看
前言
在之前创建的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函数中一般会做一些内存释放的操作,这里只是打印信息而已。
至此分析完了整个执行流程。
相关文章推荐
- linux设备驱动模型一三基础结构之Ktype
- linux设备驱动模型二【转】
- linux设备驱动模型七之driver
- Linux设备驱动模型
- [23]_Linux设备驱动模型(重要)
- Linux设备驱动模型概述
- linux设备驱动模型框架
- linux设备驱动模型-kobject
- linux设备驱动归纳总结(八)3设备模型的分层与面向对象
- Linux设备驱动模型探究--3(device)
- linux设备驱动模型里两个重要的数据结构:class和class_device
- linux设备驱动模型一三基础结构之Kobject
- linux设备驱动之字符设备驱动模型(2)
- linux设备驱动模型转载
- linux设备驱动模型一三基础结构之Kset
- Linux设备驱动模型-Kobject
- linux设备驱动中重要的3个数据结构 &&Linux设备驱动模型几个基本数据结构模型:kobject,kset,subsystem
- linux设备驱动模型架构分析(一)——概述
- linux设备驱动学习(11) linux设备模型2
- Linux设备驱动工程师之路——设备模型(下)上层模型