Linux设备驱动模型-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: 通过该回调,做最后的收尾工作,比如释放掉申请的内存等。
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代表该属性的权限。可读, 可写, 读写。
示例
#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_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。
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函数中。
补充
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相关推荐
- linux 统一设备模型 pci,Linux设备驱动模型摘抄
Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄Linux设备驱动模型摘抄 Linux设备驱动模型摘抄(1) Linux统一设备模型 简介 Li ...
- Linux设备驱动模型1——简介和底层架构
以下内容源于朱有鹏<物联网大讲堂>课程的学习整理,如有侵权,请告知删除. 一.linux设备驱动模型简介 1.什么是设备驱动模型? (1)类class.总线bus.设备device.驱动d ...
- Linux设备驱动模型之platform(平台)总线详解
/********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...
- Linux中kobject的作用,Linux设备驱动模型-- 数据结构Kset/KObject
前言 Kset和kobject是Linux设备驱动模型中的核心数据结构,其主要作用是将系统中的设备抽象出来,以树状结构组织,方便系统统一管理. 而这个统一管理的地方,就是sysfs,先放一张示例图,阐 ...
- Linux设备驱动模型三 kset
Linux设备驱动模型三 kset 1 kset数据结构 kset的定义在前文已有描述,我们再回顾一下: [cpp] view plain copy struct kset { /*与子kobject ...
- Linux设备驱动模型二 kobject
Linux设备驱动模型二 kobject 1 kobject 1.1 kobject数据结构 kobject是sysfs文件系统的基础数据结构,它定义在include/linux/kobjec.h中 ...
- 五.linux设备驱动模型
站在设备驱动这个角度分析,设备驱动模型是如何构建出来,起到什么作用,认识它并在写驱动的时候去利用设备驱动模型 目录 一.linux 设备驱动模型简介 1.1. 什么是设备驱动模型 1.2. 为什么需要 ...
- 整理--linux设备驱动模型
知识整理–linux设备驱动模型 以kobject为底层,组织类class.总线bus.设备device.驱动driver等高级数据结构,同时实现对象引用计数.维护对象链表.对象上锁.对用户空间的表示 ...
- linux设备驱动模型及其他,Linux设备驱动模型
Linux设备驱动模型,主要函数分析 整个驱动模型中,最核心的三个函数分别是 __bus_register.driver_register.device_register int __bus_regi ...
- linux平台设备驱动模型是什么意思,Linux设备驱动模型之我理解
点击(此处)折叠或打开 /* my_bus.c */ #include #include #include #include #include #include "my_bus.h&qu ...
最新文章
- Android ProGuard使用要点
- App3种开发方式的优劣分析:原生、混合和H5
- Microwindows及基于Nano-X的简单程序开发
- Tabcontrol动态添加TabPage(获取或设置当前选项卡及其属性)
- 各种编程语言,Linux命令行播放,Bio-Linux,Markdown编辑器等
- 数据分析工具测评!被Excel打过的“耳光”,现在可以还回去了
- linux log4j 使用
- SAP ALV 负号前置
- ArcGIS Pro中的拓扑检查
- axis调用webservice服务
- java 出路 xls_java读取excel之xlsl超大文件
- 一包辣条如何逆袭,从屌丝品牌成为有逼格的产品?
- ip_rcv ip_rcv_finish
- 虹科QA | SWCF2022 12月6日演讲笔记:C波段卫星与5G之间的干扰排查及解决方案
- 图表——SM2密钥协商与ECMQV对比
- html5 ins标签,HTML ins标签常用的用法有哪些?HTML ins常用属性的介绍
- 技术拯救的网瘾少年,安全盒子王松的执念 | 宅客故事
- 井通区块链数据上链介绍
- 如何利用能源收获来缓解智能家居的安装挑战?
- 车间和仓库可以一起吗_仓库与车间交接之规定