您的位置:首页 > 运维架构 > Linux

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函数中一般会做一些内存释放的操作,这里只是打印信息而已。

至此分析完了整个执行流程。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  kobject ktype sys