新闻资讯
Group news
青岛广盛源肥业有限公司    您的位置: 首页  >  新闻资讯  >  正文

需要了解Linux设备管理_kobject_kset_kobj_type等问题

2019年10月12日 文章来源:网络整理 热度:176℃ 作者:刘英

Linux内核大量使用面向对象的设计思想,通过追踪源码,我们甚至可以使用面向对象语言常用的UML类图来分析Linux设备管理的"类"之间的关系。这里以4.8.5内核为例从kobject,kset,ktype的分析入手,进而一探内核对于设备的管理方式

需要了解Linux设备管理_kobject_kset_kobj_type等问题

container_of宏

这个宏几乎是linux数据结构的基础,Linux中的链表与传统的链表不同,其链表的节点本身并不包含任何数据,任何想要插入到链表的数据只需要包含一个事先写好的节点

//include/linux/types.h184 struct list_head { 185 struct list_head *next, *prev;186 };

需要了解Linux设备管理_kobject_kset_kobj_type等问题

但是,使用这种通用的链表的第一个问题就是如何根据一个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.h 63 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_RELEASE 72 struct delayed_work release; 73 #endif 74 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) : "", 227 kobj->kset ? kobject_name(&kobj->kset->kobj) : ""); 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

上一篇:作为Linux内核关键的调试技术,可以修改内核定时器来定位系统僵死问题


下一篇:Linux设备管理从dev_add说起

友情链接
Links