Linux设备管理(一)_kobject, kset,ktype分析
Linux设备管理(一)_kobject, kset,ktype分析
转载:https://www.cnblogs.com/xiaojiang1025/p/6193959.html
Linux内核大量使用面向对象的设计思想,通过追踪源码,我们甚至可以使用面向对象语言常用的UML类图来分析Linux设备管理的"类"之间的关系。这里以4.8.5内核为例从kobject,kset,ktype的分析入手,进而一探内核对于设备的管理方式
container_of宏
这个宏几乎是linux数据结构的基础,Linux中的链表与传统的链表不同,其链表的节点本身并不包含任何数据,任何想要插入到链表的数据只需要包含一个事先写好的节点
//include/linux/types.h
184 struct list_head {
185 struct list_head *next, *prev;
186 };
但是,使用这种通用的链表的第一个问题就是如何根据一个list_head成员来找到相应的数据,Linux社区的大神们早就找到了相应的方法,就是利用下面这个container_of宏,只需要输入成员指针ptr,包含该成员的结构体类型type,以及该成员在结构体中名字name就可以返回包含ptr的type类型的结构首地址,这个宏充分利用了C语言直接操作内存的特性。需要注意的是,如果单纯为了得到地址只需要ptr-&((type* 0)->member)
,内核的写法其实还利用了编译器的类型检查机制做了一份校验工作,即如果传入的ptr类型和type->member的类型不匹配,会报错,
//include/linux/kernel.h14 #define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER) 830 #define container_of(ptr, type, member) ({ \
831 const typeof( ((type *)0)->member ) *__mptr = (ptr); \
832 (type *)( (char *)__mptr - offsetof(type,member) );})
kobject结构
Linux内核中有大量的驱动,而这些驱动往往具有类似的结构,根据面向对象的思想,我们就可以将这些共同的部分提取为父类,这个父类就是kobject,也就是驱动编程中使用的.ko文件的由来,下面这张图是我根据内核源码的kobject绘制的简单的UML图,从中可以看出,kobject包含了大量的设备必须的信息,而三大类设备驱动都需要包含这个kobject结构,也就是"继承"自kobject。一个kobject对象就对应sys目录中的一个设备。
内核源码中的kobject结构定义如下
//include/linux/kobject.h63 struct kobject { 64 const char *name;65 struct list_head entry;66 struct kobject *parent;67 struct kset *kset;68 struct kobj_type *ktype;69 struct kernfs_node *sd; /* sysfs directory entry */70 struct kref kref;71 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE72 struct delayed_work release;73 #endif74 unsigned int state_initialized:1;75 unsigned int state_in_sysfs:1;76 unsigned int state_add_uevent_sent:1;77 unsigned int state_remove_uevent_sent:1;78 unsigned int uevent_suppress:1;79 };
这个结构中,
struct kobject
--64-->name表示kobject对象的名字,对应sysfs下的一个目录。
--65-->entry是kobject中插入的head_list结构,
--66-->parent是指向当前kobject父对象的指针,体现在sys结构中就是包含当前kobject对象的目录对象,
--67-->kset表示当前kobject对象所属的集合,
--68-->ktype表示当前kobject的类型。
--69-->sd用于表示VFS文件系统的目录项,是设备与文件之间的桥梁,sysfs中的符号链接就是通过kernfs_node内的联合体实现的。
--70-->kref是对kobject的引用计数,当引用计数为0时,就回调之前注册的release方法释放该对象。
--74-->state_initialized:1初始化标志位,在对象初始化时被置位,表示对象是否已经被初始化。
--75-->state_in_sysfs:1表示kobject对象在sysfs中的状态,在对应目录中被创建则置1,否则为0。
--76-->state_add_uevent_sent:1是添加设备的uevent事件是否发送标志,添加设备时会向用户空间发送uevent事件,请求新增设备。
--77-->state_remove_uevent_sent:1是删除设备的uevent事件是否发送标志,删除设备时会向用户空间发送uevent事件,请求卸载设备
kobject操作
4.8.5的内核在lib/koject.c等源码中定义了一系列对kobject操作的函数,这里只列出最简单的几个
初始化kobject
187 static void kobject_init_internal(struct kobject *kobj)188 { 189 if (!kobj)190 return;191 kref_init(&kobj->kref);192 INIT_LIST_HEAD(&kobj->entry);193 kobj->state_in_sysfs = 0;194 kobj->state_add_uevent_sent = 0; 195 kobj->state_remove_uevent_sent = 0;196 kobj->state_initialized = 1;197 } 325 void kobject_init(struct kobject *kobj, struct kobj_type *ktype)326 {327 char *err_str;...344 kobject_init_internal(kobj); 345 kobj->ktype = ktype;346 return;...351 }
注册kobject
//添加kobject到内核200 static int kobject_add_internal(struct kobject *kobj)201 {202 int error = 0;203 struct kobject *parent;...214 parent = kobject_get(kobj->parent);215 216 /* join kset if set, use it as parent if we do not already have one */217 if (kobj->kset) {218 if (!parent)219 parent = kobject_get(&kobj->kset->kobj);220 kobj_kset_join(kobj);221 kobj->parent = parent;222 }223 224 pr_debug("kobject: '%s' (%p): %s: parent: '%s', set: '%s'\n",225 kobject_name(kobj), kobj, __func__,226 parent ? kobject_name(parent) : "<NULL>",227 kobj->kset ? kobject_name(&kobj->kset->kobj) : "<NULL>");228 229 error = create_dir(kobj);...246 kobj->state_in_sysfs = 1;247 248 return error;249 }354 static __printf(3, 0) int kobject_add_varg(struct kobject *kobj, 355 struct kobject *parent,356 const char *fmt, va_list vargs)357 {358 int retval;...365 kobj->parent = parent;366 return kobject_add_internal(kobj);367 }394 int kobject_add(struct kobject *kobj, struct kobject *parent,395 const char *fmt, ...)396 {397 va_list args;398 int retval;...410 va_start(args, fmt);411 retval = kobject_add_varg(kobj, parent, fmt, args);412 va_end(args);413 414 return retval;415 }
初始化并注册kobject
429 int kobject_init_and_add(struct kobject *kobj, struct kobj_type *ktype, 430 struct kobject *parent, const char *fmt, ...)431 { 432 va_list args; 433 int retval;434 435 kobject_init(kobj, ktype);436 437 va_start(args, fmt);438 retval = kobject_add_varg(kobj, parent, fmt, args);439 va_end(args);440 441 return retval;442 }
注销kobject
569 void kobject_del(struct kobject *kobj)570 { 571 struct kernfs_node *sd;572 573 if (!kobj)574 return; 575 576 sd = kobj->sd; 577 sysfs_remove_dir(kobj);578 sysfs_put(sd);579 580 kobj->state_in_sysfs = 0;581 kobj_kset_leave(kobj);582 kobject_put(kobj->parent);583 kobj->parent = NULL;584 }
kobject计数加一
//lib/kobject.c 591 struct kobject *kobject_get(struct kobject *kobj)592 {593 if (kobj) {594 if (!kobj->state_initialized)595 WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "596 "initialized, yet kobject_get() is being "597 "called.\n", kobject_name(kobj), kobj);598 kref_get(&kobj->kref);599 }600 return kobj;601 }
kobject计数减一
//将kobject对象的引用计数加1,同时返回该对象指针。
//include/linux/kref.h40 static inline void kref_get(struct kref *kref) 41 {42 /* If refcount was 0 before incrementing then we have a race43 * condition when this kref is freeing by some other thread right now.44 * In this case one should use kref_get_unless_zero()45 */ 46 WARN_ON_ONCE(atomic_inc_return(&kref->refcount) < 2);47 }//lib/kobject.c591 struct kobject *kobject_get(struct kobject *kobj) 592 {593 if (kobj) {...598 kref_get(&kobj->kref);599 }600 return kobj;601 }
//将kobject对象的引用计数加1,如果减为零就释放
//include/linux/kref.h67 static inline int kref_sub(struct kref *kref, unsigned int count, 68 void (*release)(struct kref *kref))69 {70 WARN_ON(release == NULL);71 72 if (atomic_sub_and_test((int) count, &kref->refcount)) {73 release(kref);74 return 1;75 }76 return 0;77 }96 static inline int kref_put(struct kref *kref, void (*release)(struct kref *kref)) 97 {98 return kref_sub(kref, 1, release);99 }//lib/kobject.c684 void kobject_put(struct kobject *kobj) 685 {686 if (kobj) {...691 kref_put(&kobj->kref, kobject_release);692 }693 }
kset结构
kset表示一组kobject的集合,kobject通过kset组织成层次化的结构,所有属于该kset的kobjetc结构的parent指针指向kset包含的kobject对象,构成一个父子层次关系这些kobject可以是不同或相同的类型(kobj_type)。sysfs中的设备组织结构很大程度上都是根据kset进行组织的,比如"/sys/drivers"目录就是一个kset对象,包含系统中的驱动程序对应的目录,驱动程序的目录又kobject表示。比如在平台设备模型中,当我们注册一个设备或驱动到平台总线,其实是将对应的kobject挂接到platform总线的kset上,每种总线都是维护两条链表(两个kset),一条用于链接挂接在上面的驱动(驱动kset),一条用于链接挂接在上面的设备(设备kset)。
//include/linux/kobject.h
168 struct kset {
169 struct list_head list;
170 spinlock_t list_lock;
171 struct kobject kobj;
172 const struct kset_uevent_ops*uevent_ops;
173 };
下面简单分析一下其中的成员
struct kset
--169-->list_head还是那个用来挂在链表上的结构,包含在一个kset的所有kobject构成了一个双向循环链表,list_head就是这个链表的头部,这个链表用来连接第一个和最后一个kobject对象,第一个kobjetc使用entry连接kset集合以及第二个kobject对象,第二个kobject对象使用entry连接第一个kobject对象和第三个kobject对象,依次类推,最终形成一个kobject对象的链表
--171-->kobj(171)是归属于该kset的所有的kobject的共有parent,这个parent就是体现内核设备组织结构的关键,同时,kset的引用计数就是内嵌的kobject对象的引用次数。
kset操作
下面是几个关于kset的基础操作方法
初始化kset
//lib/kobject.c187 static void kobject_init_internal(struct kobject *kobj) 188 {189 if (!kobj)190 return;191 kref_init(&kobj->kref);192 INIT_LIST_HEAD(&kobj->entry);193 kobj->state_in_sysfs = 0;194 kobj->state_add_uevent_sent = 0;195 kobj->state_remove_uevent_sent = 0;196 kobj->state_initialized = 1;197 }767 void kset_init(struct kset *k) 768 {769 kobject_init_internal(&k->kobj);770 INIT_LIST_HEAD(&k->list);771 spin_lock_init(&k->list_lock);772 }
注册kset
//lib/kobject.c809 int kset_register(struct kset *k) 810 {811 int err;812 813 if (!k)814 return -EINVAL;815 816 kset_init(k);817 err = kobject_add_internal(&k->kobj);818 if (err)819 return err;820 kobject_uevent(&k->kobj, KOBJ_ADD);821 return 0;822 }
注销kset
//lib/kobject.c829 void kset_unregister(struct kset *k)830 {831 if (!k)832 return;833 kobject_del(&k->kobj);834 kobject_put(&k->kobj);835 }
kset计数加一
//include/linux/kobject.h
187 static inline struct kset *kset_get(struct kset *k)
188 {
189 return k ? to_kset(kobject_get(&k->kobj)) : NULL;
190 }
kset计数减一
192 static inline void kset_put(struct kset *k)
193 {
194 kobject_put(&k->kobj);
195 }
kobj_type结构
//include/linux/kobject.h
116 struct kobj_type {
117 void (*release)(struct kobject *kobj);
118 const struct sysfs_ops *sysfs_ops;
119 struct attribute **default_attrs;
120 const struct kobj_ns_type_operations *(*child_ns_type)(struct kobject *kobj);
121 const void *(*namespace)(struct kobject *kobj);
122 };
这个结构主要是表征kobject的类型,
struct ktype
--117-->release是一个释放kobject对象的接口,有点像面向对象中的析构。
--118-->sysfs_ops是操作kobject的方法集,
//include/linux/sysfs.h29 struct attribute {30 const char *name;31 umode_t mode;32 #ifdef CONFIG_DEBUG_LOCK_ALLOC 33 bool ignore_lockdep:1; 34 struct lock_class_key *key; 35 struct lock_class_key skey; 36 #endif37 };209 struct sysfs_ops {
210 ssize_t (*show)(struct kobject *, struct attribute *, char *);
211 ssize_t (*store)(struct kobject *, struct attribute *, const char *, si
212 };
struct sysfs_ops
--210-->我们在使用cat echo等工具(read()/write()系统调用)进行读写sysfs中相应驱动的属性时,其实就是回调驱动的show()和store()。由此可见,对同一类型的kobject操作会回调同一个kobj_type的方法
//include/linux/kobject.h
197 static inline struct kobj_type *get_ktype(struct kobject *kobj)
198 {
199 return kobj->ktype;
200 }
从这个函数中可以看出,4.8.5提取kobject的kobj_type的时候直接提取kobject的,我还测试过3.14版本的,也是这种写法,不过网上还有下面的这种get_ktype的实现,还没找到具体是哪个版本,显然,这个版本中kset中的ktype这个类型优先于 kobject 自身中的 ktype 。因此在典型的应用中, 在 struct kobject 中的 ktype 成员被设为 NULL, 而 kset 中的ktype是实际被使用的。
static inline struct kobj_type * get_ktype(struct kobject * k){if (k->kset && k->kset->ktype)return k->kset->ktype;else return k->ktype;}
结构框图
kobject,kset是Linux设备管理中的基本结构体,但在实际操作中我们几乎不会实际操作这些结构,因为他们本身并不具有针对某一个具体设备或驱动的信息,在Linux内核中,这两个结构都是被包含具体的设备结构中,比如cdev,gendisk等,从面向对象的角度考虑,就是每一类设备都可以看作这两个结构的子类。
通过上面的分析,我们可以看出这三者之间的关系,并画出下面的结构框图,sysfs中的上目录结构就是根据kset之间的数据组织方式进行呈现的。
分类: Linux驱动开发
标签: kobject, kset, kobj_type
« 上一篇: Linux字符设备驱动框架
» 下一篇: Linux设备管理(二)_从cdev_add说起
Linux设备管理(一)_kobject, kset,ktype分析相关推荐
- Linux设备管理: kobject、kset、ktype分析
Linux 内核大量使用面向对象的设计思想,通过追踪源码,我们甚至可以使用面向对象语言常用的 UML 类图来分析 Linux 设备管理的"类"之间的关系.这里以 4.14 内核为例 ...
- Linux设备管理(三)_总线设备的挂接
扒完了字符设备,我们来看看平台总线设备,平台总线是Linux中的一种虚拟总线,我们知道,总线+设备+驱动是Linux驱动模型的三大组件,设计这样的模型就是将驱动代码和设备信息相分离,对于稍微复杂一点的 ...
- linux设备模型之kset/kobj/ktype分析
1. 概述 今天来聊一下Linux设备模型的基石:kset/kobject/ktype. sysfs文件系统提供了一种用户与内核数据结构进行交互的方式,可以通过mount -t sysfs sysfs ...
- Linux设备模型:kset, kobj, ktype
参照本站转载LWN文章:<kobject/kset/ktype documentation and example code updated> 本文转自:LoyenWang 目录 1. 概 ...
- linux设备管理之设备号与次设备号
linux设备管理之主设备号与次设备号 - jinzi - 博客园+ 剽窃 过来的. 记录下,以备查. 主设备号和次设备号 一个字符设备或者块设备都有一个主设备号和次设备号.主设备号和次设备号统称为 ...
- linux C函数之strdup函数分析【转】
本文转载自:http://blog.csdn.net/tigerjibo/article/details/12784823 linux C函数之strdup函数分析 一.函数分析 1.函数原型: [c ...
- unix amp; linux oralce用户 内存使用情况分析
Linux*********************************************************************************************** ...
- ARM linux的启动部分源代码简略分析
ARM linux的启动部分源代码简略分析 以友善之臂的mini2440开发板为平台,以较新的内核linux-2.6.32.7版本为例,仅作说明之用. 当内核映像被加载到RAM之后,Bootloade ...
- Linux与JVM的内存关系分析
Linux与JVM的内存关系分析 原文出处: 美团技术团队 引言 在一些物理内存为8g的服务器上,主要运行一个Java服务,系统内存分配如下:Java服务的JVM堆大小设置为6g,一个监控进程占用大约 ...
最新文章
- pyinstaller打包生成的exe文件并使用python终止后台的exe程序运行
- python在内存中生成Zip文件!
- 自定义Spark Partitioner提升es-hadoop Bulk效率
- 【Android 安全】DEX 加密 ( 阶段总结 | 主应用 | 代理 Application | Java 工具 | 代码示例 ) ★
- liferay如何debug
- 如何去掉jQWidgets中TreeGrid和Grid右下角的链接
- ArcGIS API for Silverlight 调用GP服务准备---GP模型建立、发布、测试
- LVM (Logic Volume Management,逻辑卷管理)
- MyBatis 逆向工程(MyBatis 自动生成接口以及xml)的使用
- TypeScript基础类型
- 5 个用于在 Linux 终端中查找域名 IP 地址的命令
- 人脸关键点检测face_landmark
- 新装MySql后登录出现root帐号提示mysql ERROR 1045 (28000): Access denied for use的解决办法
- 上海计算机一级考试理论,上海市计算机一级考试理论部分(上).doc
- 【手把手教你Ubuntu】Ubuntu 13.04 Linux 3D桌面完全教程,显卡驱动安装方法
- [悦读] 让听得见炮声的人来决策——《赋能》读书笔记
- 【鸿蒙】《校园通》--校园生活模块
- HashMap、ConcurrentHashMap源码解读(JDK7/8)
- 南昌大学计算机类学费软件,南昌大学2017年各专业学费一览表
- 报错:pymysql.err.IntegrityError: (1062, “Duplicate entry ‘1‘ for key ‘mm.PRIMARY‘“)
热门文章
- iOS 直播专题1-直播流程原理
- 一文回顾 Java 入门知识(上)
- BP神经网络原理简单介绍以及公式推导(矩阵形式和分量形式)
- 可视对讲系统服务器连接失败,可视对讲系统呼叫分机、门口机不通是什么问题?...
- html中显示页面布局,如何让打开的网页显示在一个页面上
- 算术编码、译码以及matlab实现
- Pwnginx – a nginx backdoor offering shell
- 5个最好的WordPress电商插件比较 - 2019年
- Linux网络操作系统期末系统复习题
- JAVA、PHP身份证、统一社会信用代码算法解析验证