Linux设备驱动之Kobject、Kset
作者:lizuobin(也是我们兼职的论坛答疑助手)
原文:
https://blog.csdn.net/lizuobin2/article/details/51523693
纠结又纠结,虽然看了一些关于kobject 和 kset 的书与文章,但是对于这两个东西,还是不太明白,又分析了一遍2.6.32.2内核的代码,结合前人的努力,终于有了一点眉目。总结一下,给自己做个笔记,也给初学者一点指引。
LDD3中说,Kobject的作用为:
1、sysfs 表述:在 sysfs 中出现的每个对象都对应一个 kobject, 它和内核交互来创建它的可见表述。
2、热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。
3、数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject实现了该结构并将其聚合在一起。
此篇文章,只分析第一条,kobject 与 kset sysfs 之间的关系。
首先,什么是kobject ,它也不过是内核里的一个结构体而已
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
......// 省略一些暂时不想看到的东西
};
那么,这个结构体有何特殊之处呢?
每一个 kobject对应文件系统 /sys 里的一个目录,目录的名字就是结构体中的name
什么又是 kset ?kset 也是个结构体而已
struct kset {
struct list_head list;
struct kobject kobj;
struct kset_uevent_ops *uevent_ops;
spinlock_t list_lock;
};
前面说了,每一个 kobj 对应文件系统 /sys 里的一个目录,那么每一个 kset 都包含了一个 kobj, 那样的话 , kset也对应于 /sys 里的一个目录
简单来说,kset 与 kobj 都是目录,既然是目录,那么应该就是一个树状结构,每一个目录都将有一个父节点:
在kset中使用kset.kobj->parent 指定
在kboject中使用 kobj->parent 指定
显然,整个树状目录结构,都是通过kobj来构建的,只不过有些kobj嵌在Kset里,分析目录结构时把kset当成一个普通的kobj会好理解很多。
那么kset 有何特殊之处呢?
我们可以看到 kset 结构中有一个链表,它目录下的 一些相同类型的kobj会在创建时链入链表,也就是说Kset是一些kobj的集合,光说还是比较抽象的,那么我们就来看看 /sys 目录底下的这些东西,哪些是kset? 哪些是kobj 结构又是怎样的。
看过代码的应该知道,想要在/sys 目录下创建目录,那就要构造 kset 或者 kobj ,设置并注册。
对于kobject设置注册方法是:
kobject_create_and_add
kobject_init_and_add
对于kset设置注册方法是:
kset_create_and_add
我在这3个函数中增加了prink打印语句,打印内核创建的每一个 kobj 或者 kset 的名字,以及父节点的名字,甚至它指向的kset的kobj的名字。
原本我以为,较高层次的目录会是kset,因为它是个集合嘛,然而并不全是。打印信息如下:
the kset name is devices,no parent
the kset name is system,parent's name is devices
the kobject name is virtual,parent'
the kset name is bus,no parent
the kset name is class,no parent
the kset name is module,no parent
the kobject name is fs,no parent
the kobject name is dev,no parent
the kobject name is block,parent's name is dev
the kobject name is char,parent'
the kobject name is firmware,no parent
the kobject name is kernel,no parent
the kset name is slab,parent's name is kernel
写着no parent的,在/sys/目录下可以找到它们,对于devices、bus、class、module它们是kset;对于fs、dev、firmware、kernel、block 它们是kobj
而且,我们还可以发现
1、 kset 底下可以放 kset (但是无法链入链表,分析代码时会知道)
the kset name is devices,no parent
the kset name is system,parent's name is devices
2、 kset 底下可以放 kobj (可以链入链表,也可以不链入)
the kset name is devices,no parent
the kobject name is virtual,parent's name is devices
3、 kobject 底下可以放 kset (显然没链表的概念了)
the kobject name is kernel,no parent
the kset name is slab,parent's name is kernel
4、 kobj 底下放 kobj (同样没有链表的概念)
the kobject name is dev,no parent
the kobject name is block,parent's name is dev
如下图:黄色代表Kset,蓝色代表Kobject
至此我们对kset kobj它们之间的联系应该有一个比较浅显的认识了。
下面分析代码进一步摸索 , 先把图贴上来,虚线表示可能的其它一些连接情况。
先看kobject_create_and_add的实现:
struct kobject*kobject_create_and_add(const char *name, struct kobject *parent)
{
struct kobject *kobj;
kobj = kobject_create();
retval = kobject_add(kobj, parent, "%s", name);
return kobj;
}
再看它的调用过程:
kobject_create_and_add
kobject_create // kobj =kzalloc(sizeof(*kobj), GFP_KERNEL);
kobject_init //kobj->ktype = ktype;
kobject_init_internal // kref_init(&kobj->kref);
kobject_add
kobject_add_varg // retval = kobject_set_name_vargs(kobj,fmt, vargs); // kobj->parent = parent;
kobject_add_internal
if (kobj->kset) { // kobj->kset == NULL 不执行
if (!parent)
parent =kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent;
}
create_dir
kobject_create_and_add 函数从构建一个kobject到在sysfs中创建路径一气呵成,其中没有关于kset的设置,仅仅是设置了parent ktype
如果 kobject_create_and_add 传入的 parent 参数是一个普通的kobject ,那么就与应于图中的③与⑤的关系
如果 kobject_create_and_add 传入的 parent 参数是一个kset->kobject,那么就与应于图中的③与④的关系
priv->kobj.kset =bus->p->drivers_kset; // 设置它所属的Kset
error = kobject_init_and_add(&priv->kobj, &driver_ktype,NULL, "%s", drv->name);
再看kobject_init_and_add的实现以及调用:
int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype,
struct kobject *parent, const char *fmt, ...)
{
kobject_init(kobj, ktype);
retval = kobject_add_varg(kobj, parent, fmt, args);
}
kobject_init_and_add
kobject_init // kobj->ktype = ktype;
kobject_init_internal // kref_init(&kobj->kref);
kobject_add_varg // retval = kobject_set_name_vargs(kobj,fmt, vargs); // kobj->parent = parent;
kobject_add_internal
if (kobj->kset) { // 将kobject添加到它所指向的kset->list中
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent; // 如果没有parent 将它所属的kset->kobj作为它的parent
}
create_dir
与kobject_create_and_add 相比,就是少了构建kobkject,然而这样给了我们设置kset的机会,同时往往不会设置parent,对应于图中的①与④的关系
再看kset_create_and_add的实现以及调用:
struct kset*kset_create_and_add(const char *name,
struct kset_uevent_ops *uevent_ops,
struct kobject *parent_kobj)
{
...
kset= kset_create(name, uevent_ops, parent_kobj);
...
error= kset_register(kset);
}
kset_create_and_add
kset_create
kset = kzalloc(sizeof(*kset), GFP_KERNEL);
retval = kobject_set_name(&kset->kobj, name);
kset->uevent_ops = uevent_ops;
kset->kobj.parent = parent_kobj;
kset->kobj.ktype = &kset_ktype;
kset->kobj.kset = NULL;
kset_register
kset_init(k);
err = kobject_add_internal(&k->kobj);
parent = kobject_get(kobj->parent);
if (kobj->kset) { // kobj->kset==NULL 不执行
....
}
error = create_dir(kobj);
kset_create_and_add 无法将创建kset->kobj.kset 指向任何kset
但是kset->kobj.parent 还是能和kobj.parent指向普通的kobj 或者包含在kset里的kobj。
如果 kset_create_and_add 传入的 parent 参数 是一个普通的kobject ,那么就对应于图中的④与⑤的关系
如果 kset_create_and_add 传入的 parent 参数 是一个kset->kobject,那么就对应于图中的②与④的关系
还有一种情况就是,创建一个 kset 并设置kset.kobject.kset
然后调用 kset_register
kset_register的调用过程:
kset_register
kset_init(k);
err = kobject_add_internal(&k->kobj);
parent = kobject_get(kobj->parent);
if(kobj->kset) { // 将kobject 添加到它所指向的kset->list中
if (!parent)
parent = kobject_get(&kobj->kset->kobj);
kobj_kset_join(kobj);
kobj->parent = parent; // 如果没有parent 将它所属的kset->kobj作为它的parent
}
error = create_dir(kobj);
对应于图中④与⑥的关系。
上面代码的细节,比如如何在/sys/创建路径请参考:
http://blog.csdn.net/lizuobin2/article/details/51511336
到此,我们应该对 kobject kset sysfs 之间的目录关系比较清楚了,但是我们至少还应该看看ktype。
kobject 包括了kset:
struct kobject {
const char *name;
struct list_head entry;
struct kobject *parent;
struct kset *kset;
struct kobj_type *ktype;
struct kref kref;
……
};
ktype 由一个release函数、一个sysfs_ops、一个指针数组(二维指针)组成:
struct kobj_type {
void(*release)(struct kobject *kobj);
struct sysfs_ops *sysfs_ops;
struct attribute **default_attrs;
//struct attribute *default_attrs[]
};
1、release 函数
每一个Kobject 都必须有一个release方法,有意思的是,release 函数并没有包括在kobject自身内,而是包含在它的结构体成员Ktype内。而且kobject在调用release之前应该保持稳定(不明白抄自LDD3)。
2、
struct attribute **default_attrs
struct attribute {
const char *name;
struct module *owner;
mode_t mode;
};
default_attrs 指向的地方是个指针数组,这些指针的类型为attribute ,那么这些attribute就是该kobject的属性了,name是属性的名字,在kobject目录下表现为文件 ,owner 指向模块的指针(如果有的话),那么该模块负责实现这些属性。mode 是保护位,通常是S_IRUGO,可写的则用S_IWUSR 仅为root提供写权限。default_attrs最后一个元素必须为0,要不然它找不着北~
3、sysfs_opes 实现属性的方法
struct sysfs{
ssize_t *show(struct kobject *kobject, struct attribute*attr,char *buf);
ssize_t *store(struct kobject *kobject, struct attribute*attr,char *buf, size_t size);
}
在内核里,一类设备往往使用相同的show , store函数。
附上一个例子:在linux2.6.32.2下编译通过
#include<linux/device.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/string.h>
#include <linux/sysfs.h>
#include <linux/stat.h>
MODULE_LICENSE("Dual BSD/GPL");
---------------------------------------default_attrs---------------------------
/*对应于kobject的目录下的一个文件,Name成员就是文件名*/
struct attribute test_attr = {
.name = "kobj_config",
.mode = S_IRWXUGO,
};
static struct attribute *def_attrs[] ={
&test_attr,
NULL,
};
---------------------------------------sysfs_ops-------------------------------
/*当读文件时执行的操作*/
ssize_t kobj_test_show(struct kobject*kobject, struct attribute *attr,char *buf)
{
printk("have show.\n");
printk("attrname:%s.\n", attr->name);
sprintf(buf,"%s\n",attr->name);
return strlen(attr->name)+2;
}
/*当写文件时执行的操作*/
ssize_t kobj_test_store(struct kobject*kobject,struct attribute *attr,const char *buf, size_t count)
{
printk("havestore\n");
printk("write: %s\n",buf);
return count;
}
//kobject对象的操作
struct sysfs_ops obj_test_sysops =
{
.show = kobj_test_show,
.store = kobj_test_store,
};
---------------------------------------release-------------------------------------------------------
/*release方法释放该kobject对象*/
void obj_test_release(struct kobject*kobject)
{
printk("eric_test: release .\n");
}
---------------------------------------kobj_type-----------------------------------------------------
/*定义kobject对象的一些属性及对应的操作*/
struct kobj_type ktype =
{
.release = obj_test_release,
.sysfs_ops=&obj_test_sysops,
.default_attrs=def_attrs,
};
struct kobject kobj;//声明kobject对象
static int kobj_test_init(void)
{
printk("kboject test init.\n");
kobject_init_and_add(&kobj,&ktype,NULL,"kobject_test");/
return 0;
}
static void kobj_test_exit(void)
{
printk("kobject test exit.\n");
kobject_del(&kobj);
}
module_init(kobj_test_init);
module_exit(kobj_test_exit);
执行程序,可以看到在sys目录多了kobject test目录,里面有kobj_config文件并且可读。
核心结论:
1、sys 目录下的层次结构依赖于kobject.parent ,未指定parent时,默认使用 kobject.kset.kobject 作为parent,如果都没有,就出现在/sys 目录下。
2、该 kobject 目录下的属性文件依赖于 kobject.ktype
--END--
关注公众号百问科技(ID:baiwenkeji)第一时间阅读嵌入式干货。
技术交流加个人威信13266630429,验证:博客园
Linux设备驱动之Kobject、Kset的更多相关文章
- linux设备驱动模型之Kobject、kobj_type、kset【转】
本文转载自:http://blog.csdn.net/fengyuwuzu0519/article/details/74838165 版权声明:本文为博主原创文章,转载请注明http://blog.c ...
- linux设备驱动归纳总结(八):4.总线热插拔【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-110774.html linux设备驱动归纳总结(八):4.总线热插拔 xxxxxxxxxxxxxxx ...
- linux设备驱动归纳总结(八):1.总线、设备和驱动【转】
本文转载自:http://blog.chinaunix.net/uid-25014876-id-109733.html linux设备驱动归纳总结(八):1.总线.设备和驱动 xxxxxxxxxxxx ...
- Linux 设备驱动 Edition 3
原文网址:http://oss.org.cn/kernel-book/ldd3/index.html Linux 设备驱动 Edition 3 By Jonathan Corbet, Alessand ...
- linux 设备驱动概述
linux 设备驱动概述 目前,Linux软件工程师大致可分为两个层次: (1)Linux应用软件工程师(Application Software Engineer): 主要利用C库函数和 ...
- Linux设备驱动模型之platform(平台)总线详解
/********************************************************/ 内核版本:2.6.35.7 运行平台:三星s5pv210 /*********** ...
- LINUX设备驱动模型之class
转自 https://blog.csdn.net/qq_20678703/article/details/52754661 1.LINUX设备驱动模型中的bus.device.driver,.其中bu ...
- Linux设备驱动模型底层架构及组织方式
1.什么是设备驱动模型? 设备驱动模型,说实话这个概念真的不好解释,他是一个比较抽象的概念,我在网上也是没有找到关于设备驱动模型的一个定义,那么今天就我所学.所了解 到的,我对设备驱动模型的一个理解: ...
- Linux 设备驱动模型
Linux系统将设备和驱动归一到设备驱动模型中了来管理 设备驱动程序功能: 1,对硬件设备初始化和释放 2,对设备进行管理,包括实参设置,以及提供对设备的统一操作接口 3,读取应用程序传递给设备文件的 ...
随机推荐
- Win7如何自定义鼠标右键菜单 添加新建EXCEL文档
鼠标右键添加新建EXCEL文档.reg Windows Registry Editor Version 5.00 [HKEY_CLASSES_ROOT\.xls] "Content Type ...
- python(28)- 面向对象练习Ⅱ
题目一:总结 1.什么是绑定到对象的方法,如何定义,如何调用,给谁用?有什么特性? 类内定义的函数,不经装饰器装饰,被实例化对象调用,会默认传入一个self参数,对象将会传递给self: 定义方式比较 ...
- linux之return和exit引发的大问题(vfork和fork)
在coolshell.cn上看到的一个问题.为此拿来研究一下. 首先 看看return和exit的差别 在linux上分别跑一下这个代码 int main() { return 0; //exit(0 ...
- Numpy数组计算
NumPy是高性能科学计算和数据分析的基础包.它是pandas等其他各种工具的基础. NumPy的主要功能 ndarray,一个多维数组结构,高效且节省空间 无需循环对整组数据进行快速运算的数学函数 ...
- import caffe时出错:can not find module skimage.io
import caffe时出错:can not find module skimage.io //以下内容在ubuntu16.4上实际验证过.注意大小写的.----20170605 在命令行输入Py ...
- 使用Auto Layout处理比例间距问题
使用Auto Layout处理比例间距问题 Auto Layout 是一个掌握起来很具有挑战性的东西.iOS 9引入的 Stack Views和 layout 锚点有一些帮助,但是明白如何创建特定的 ...
- HDU 5384 Danganronpa (2015年多校比赛第8场)
1.题目描写叙述:点击打开链接 2.解题思路:本题利用字典树解决.本题要求查找全部的B[j]在A[i]中出现的总次数.那么我们能够建立一颗字典树,将全部的B[j]插入字典树,因为一个串的全部字串相当于 ...
- 让EasyDarwin只支持RTP over TCP传输
我们经常需要EasyDarwin服务器支持公网流媒体传输,但很多时候,播放器默认都是通过RTP over UDP的形式在RTSP SETUP中请求,往往都以在内网接收不到UDP数据失败结束,那么我们如 ...
- spring 集成 mybatis 后数据源初始化失败问题分析
问题背景: 项目使用spring,springmvc框架,后边需操作关系数据库,选择了mybatis + durid,集成mybatis后,项目一直启动失败.错误的原因是dataSource初始化的时 ...
- eacharts 根据后台数据生成柱状图
说明:开发环境vs2012 ,asp.net mvc4项目,c#语言 1.效果图 2.HTML 前端代码 <%@ Page Language="C#" AutoEventWi ...