前言

在之前创建的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_filesysfs_create_file_nssysfs_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;elseops = &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);                            //得到注册时候的opshas_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);elseerror = 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);         -------------------------Assize_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_putkobject_releasekobject_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相关推荐

  1. linux 统一设备模型 pci,Linux设备驱动模型摘抄

    Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄 Linux设备驱动模型摘抄(1) Linux统一设备模型 简介 Li ...

  2. Linux设备驱动模型1——简介和底层架构

    以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...

  3. Linux设备驱动模型之platform(平台)总线详解

    /********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...

  4. Linux中kobject的作用,Linux设备驱动模型-- 数据结构Kset/KObject

    前言 Kset和kobject是Linux设备驱动模型中的核心数据结构,其主要作用是将系统中的设备抽象出来,以树状结构组织,方便系统统一管理. 而这个统一管理的地方,就是sysfs,先放一张示例图,阐 ...

  5. Linux设备驱动模型三 kset

    Linux设备驱动模型三 kset 1 kset数据结构 kset的定义在前文已有描述,我们再回顾一下: [cpp] view plain copy struct kset { /*与子kobject ...

  6. Linux设备驱动模型二 kobject

    Linux设备驱动模型二 kobject 1 kobject 1.1 kobject数据结构 kobject是sysfs文件系统的基础数据结构,它定义在include/linux/kobjec.h中 ...

  7. 五.linux设备驱动模型

    站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...

  8. 整理--linux设备驱动模型

    知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...

  9. linux设备驱动模型及其他,Linux设备驱动模型

    Linux设备驱动模型,主要函数分析 整个驱动模型中,最核心的三个函数分别是 __bus_register.driver_register.device_register int __bus_regi ...

  10. linux平台设备驱动模型是什么意思,Linux设备驱动模型之我理解

    点击(此处)折叠或打开 /* my_bus.c   */ #include #include #include #include #include #include "my_bus.h&qu ...

最新文章

  1. Android ProGuard使用要点
  2. App3种开发方式的优劣分析:原生、混合和H5
  3. Microwindows及基于Nano-X的简单程序开发
  4. Tabcontrol动态添加TabPage(获取或设置当前选项卡及其属性)
  5. 各种编程语言,Linux命令行播放,Bio-Linux,Markdown编辑器等
  6. 数据分析工具测评!被Excel打过的“耳光”,现在可以还回去了
  7. linux log4j 使用
  8. SAP ALV 负号前置
  9. ArcGIS Pro中的拓扑检查
  10. axis调用webservice服务
  11. java 出路 xls_java读取excel之xlsl超大文件
  12. 一包辣条如何逆袭,从屌丝品牌成为有逼格的产品?
  13. ip_rcv ip_rcv_finish
  14. 虹科QA | SWCF2022 12月6日演讲笔记:C波段卫星与5G之间的干扰排查及解决方案
  15. 图表——SM2密钥协商与ECMQV对比
  16. html5 ins标签,HTML ins标签常用的用法有哪些?HTML ins常用属性的介绍
  17. 技术拯救的网瘾少年,安全盒子王松的执念 | 宅客故事
  18. 井通区块链数据上链介绍
  19. 如何利用能源收获来缓解智能家居的安装挑战?
  20. 车间和仓库可以一起吗_仓库与车间交接之规定

热门文章

  1. js requestAnimationFrame
  2. HDU 2444 The Accomodation of Students (二分图最大匹配+二分图染色)
  3. fileurlwithpath urlwithstring 这俩有啥区别吗
  4. 控制面板中java设置项如何删除
  5. 使用 Google Analytics 跟踪 JavaScript 错误
  6. hdu 2642 Stars 树状数组
  7. asp.net gridview添加删除确认对话框
  8. Flask模板操作一:基本使用
  9. 量子计算会带来什么样的革命?
  10. 【C#】使用OWIN创建Web API