内核对象kobject和sysfs(3)——kobj分析

内核对象kobject和sysfs(3)——kobj分析


在分析kobj之前,先总结下kobj的功能:

  1. 实现结构的动态管理;
  2. 实现内核对象到sysfs的映射;
  3. 实现自定义属性的管理。

关注一下kobj的结构:

struct kobject {
	const char		*name;// 该内核对象的名称
	struct list_head	entry;// 链入kset的连接件
	struct kobject		*parent;// 指向父对象,可以为空
	struct kset		*kset; // 指向的内核对象集合,可以为空
	struct kobj_type	*ktype; // 该内核对象使用的操作集合
	struct kernfs_node	*sd; /* sysfs directory entry */
	struct kref		kref; // 该内核对象的引用计数
#ifdef CONFIG_DEBUG_KOBJECT_RELEASE
	struct delayed_work	release;
#endif
	unsigned int state_initialized:1;
	unsigned int state_in_sysfs:1;
	unsigned int state_add_uevent_sent:1;
	unsigned int state_remove_uevent_sent:1;
	unsigned int uevent_suppress:1;
};

kobject内的注释已经注释在页面上,在初次接触的时候,先关注其中name,parent,ktype, kref这三个域即可。下面详细分析:

  • name是该内核对象的名称,在其向sysfs注册的时候,显示的目录的名字;
  • parent指示了该内核对象在sysfs中的位置,如果有父类,则目录会创建在对应的父类下,如果没有,则创建在/sysfs下;
  • ktype包含了该内核对象的操作方法,包括前面提及的kref的自定义释放函数和自定义属性操作;

下面给出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);
};
struct sysfs_ops {
	ssize_t	(*show)(struct kobject *, struct attribute *, char *);
	ssize_t	(*store)(struct kobject *, struct attribute *, const char *, size_t);
};
struct attribute {
	const char		*name;
	umode_t			mode;
};

我们先大致看一下这三个结构。不做深入分析,先看一下如何操作kobj。
kobj的操作和kref的流程大致相似,包括初始化、注册、注销。

先来看看初始化:

 325 void kobject_init(struct kobject *kobj, struct kobj_type *ktype)
 326 {
 327         char *err_str;
 328 
 329         if (!kobj) {
 330                 err_str = "invalid kobject pointer!";
 331                 goto error;
 332         }
 333         if (!ktype) {
 334                 err_str = "must have a ktype to be initialized properly!\n";
 335                 goto error;
 336         }
 337         if (kobj->state_initialized) {
 338                 /* do not error out as sometimes we can recover */
 339                 printk(KERN_ERR "kobject (%p): tried to init an initialized "
 340                        "object, something is seriously wrong.\n", kobj);
 341                 dump_stack();
 342         }
 343 
 344         kobject_init_internal(kobj);
 345         kobj->ktype = ktype;
 346         return;
 347 
 348 error:
 349         printk(KERN_ERR "kobject (%p): %s\n", kobj, err_str);
 350         dump_stack();
 351 }
 352 EXPORT_SYMBOL(kobject_init); 

 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 }

可以看到,kobject_init对应kref的kref_init。函数有两个参数,一个是待初始化的内核对象,一个是该对象的操作集合。关键的步骤在344~345行。在kobject_init_internal里我们可以看到,他对内嵌的kref进行了初始化,这和上一篇描述的用法基本相同。其他的代码都是一些状态的初始化,暂且略过。不过值得提及的是INIT_LIST_HEAD宏。该宏在内核中广泛用到,用于初始化一个通用链表的头结点,将前后都指向自己。内核通过通用链表的连接件,将数据结构链入链表中加以管理,这里不再赘述。回到345行,其实发现kobject_init操作对kobj_type 的内容并不做检查。

 684 void kobject_put(struct kobject *kobj)
 685 {
 686         if (kobj) {
 687                 if (!kobj->state_initialized)
 688                         WARN(1, KERN_WARNING "kobject: '%s' (%p): is not "
 689                                "initialized, yet kobject_put() is being "
 690                                "called.\n", kobject_name(kobj), kobj);
 691                 kref_put(&kobj->kref, kobject_release);
 692         }
 693 }       
 694 EXPORT_SYMBOL(kobject_put);

kobject_put是内核对象的释放函数,对应于kref的kref_put。可以看到,kobject_put仅仅是对kref_put的一个封装而已,向kref机制注册了kobject_release函数。

 663 static void kobject_release(struct kref *kref)
 664 {
 665         struct kobject *kobj = container_of(kref, struct kobject, kref);
 666 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 667         unsigned long delay = HZ + HZ * (get_random_int() & 0x3);
 668         pr_info("kobject: '%s' (%p): %s, parent %p (delayed %ld)\n",
 669                  kobject_name(kobj), kobj, __func__, kobj->parent, delay);
 670         INIT_DELAYED_WORK(&kobj->release, kobject_delayed_cleanup);
 671         
 672         schedule_delayed_work(&kobj->release, delay);
 673 #else   
 674         kobject_cleanup(kobj);
 675 #endif  
 676 }  

由于向kref注册的函数参数只能是kref,所以必须通过container_of宏来获取到封装该kref的kobject,这也是上一篇中说明kref必须是内嵌的原因之一。只有内嵌结构,才可以使用container_of宏进行操作。container_of宏在此不再赘述。
获取到kobj的地址后,在674行释放资源。

初学者看到这里,不妨停下来,也许你会发现,kobject实际上,从这两个函数看来。就是上一篇我们自己写的内核模块的一个更专业版本。kobj_type 在这两个函数里,也就是赋值的作用,并没有起到真正的功能。因此,也许我们可以把上一篇的代码改进一下,写一个不提供任何功能的内核模块,仅仅是为了验证一下kobj拥有kref的功能,不过需要注意的是,一定要实现其中的release函数,这一点在后面详细说明。

验证之后,分析kobject_add:

 394 int kobject_add(struct kobject *kobj, struct kobject *parent,
 395                 const char *fmt, ...)
 396 {
 397         va_list args;
 398         int retval;
 399 
 400         if (!kobj)
 401                 return -EINVAL;
 402 
 403         if (!kobj->state_initialized) {
 404                 printk(KERN_ERR "kobject '%s' (%p): tried to add an "
 405                        "uninitialized object, something is seriously wrong.\n",
 406                        kobject_name(kobj), kobj);
 407                 dump_stack();
 408                 return -EINVAL;
 409         }
 410         va_start(args, fmt);
 411         retval = kobject_add_varg(kobj, parent, fmt, args);
 412         va_end(args);
 413 
 414         return retval;
 415 }
 416 EXPORT_SYMBOL(kobject_add);

 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;
 359 
 360         retval = kobject_set_name_vargs(kobj, fmt, vargs);
 361         if (retval) {
 362                 printk(KERN_ERR "kobject: can not set name properly!\n");
 363                 return retval;
 364         }
 365         kobj->parent = parent;
 366         return kobject_add_internal(kobj);
 367 }

 200 static int kobject_add_internal(struct kobject *kobj)
 201 {
 202         int error = 0;
 203         struct kobject *parent;
 204 
 205         if (!kobj)
 206                 return -ENOENT;
 207 
 208         if (!kobj->name || !kobj->name[0]) {
 209                 WARN(1, "kobject: (%p): attempted to be registered with empty "
 210                          "name!\n", kobj);
 211                 return -EINVAL;
 212         }
 213 
 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);
 230         if (error) {
 231                 kobj_kset_leave(kobj);                                                                                                                                                                      
 232                 kobject_put(parent);
 233                 kobj->parent = NULL;
 234 
 235                 /* be noisy on error issues */
 236                 if (error == -EEXIST)
 237                         WARN(1, "%s failed for %s with "
 238                              "-EEXIST, don't try to register things with "
 239                              "the same name in the same directory.\n",
 240                              __func__, kobject_name(kobj));
 241                 else
 242                         WARN(1, "%s failed for %s (error: %d parent: %s)\n",
 243                              __func__, kobject_name(kobj), error,
 244                              parent ? kobject_name(parent) : "'none'");
 245         } else
 246                 kobj->state_in_sysfs = 1;
 247 
 248         return error;
 249 }

可以看到,kobject_add有三个参数。该函数功能是将该内核对象添加到sysfs内。因此第一个参数指明了要注册的内核对象,第二个参数配置了该对象的父类,可以为NULL。第三个参数为该内核对象的名称。我之前会疑惑为什么内核对象的名称不在初始化的时候指定,现在想想,在注册的时候才指定,其实也可以理解。毕竟只有注册进了sysfs,名称才有意义。
函数的主题功能集中在kobject_add_internal内。下面逐一分析:

  • 214行对父类加以引用。parent作为本内核模块的父类,那么对于本内核模块来说是一个依赖。对其加引用保证了在自身被释放之前,父类不会被释放。为了形成对应,在注销函数内,一定会释放这个引用;
  • 229行在sysfs下创建目录和之下的属性文件,具体创建的位置,由parent决定;
  • kset暂时略过,在别的篇章详细分析。
    由此可得,kobject_add唯一作用就是在sysfs里创建层次化的目录,而这种层次都是parent带来的。逻辑上的父子概念,在sysfs内形成了目录的包含概念。
    到了这里,其实不妨对之前的测试模块添加注册和注销函数,感受下父子关系与目录子目录关系的转换。

在这之后,我们终于可以深入开始分析kobj_type。在分析之后,争取写一个真正的ktype。
release函数实际上就是自定义的kobject的释放函数。我们曾在674行调用kobject_cleanup释放obj,但没有具体分析,现代码列如下:

 615 static void kobject_cleanup(struct kobject *kobj)
 616 {
 617         struct kobj_type *t = get_ktype(kobj);
 618         const char *name = kobj->name;                                                                                                                                                                      
 619 
 620         pr_debug("kobject: '%s' (%p): %s, parent %p\n",
 621                  kobject_name(kobj), kobj, __func__, kobj->parent);
 622 
 623         if (t && !t->release)
 624                 pr_debug("kobject: '%s' (%p): does not have a release() "
 625                          "function, it is broken and must be fixed.\n",
 626                          kobject_name(kobj), kobj);
 627 
 628         /* send "remove" if the caller did not do it but sent "add" */
 629         if (kobj->state_add_uevent_sent && !kobj->state_remove_uevent_sent) {
 630                 pr_debug("kobject: '%s' (%p): auto cleanup 'remove' event\n",
 631                          kobject_name(kobj), kobj);
 632                 kobject_uevent(kobj, KOBJ_REMOVE);
 633         }
 634 
 635         /* remove from sysfs if the caller did not do it */
 636         if (kobj->state_in_sysfs) {
 637                 pr_debug("kobject: '%s' (%p): auto cleanup kobject_del\n",
 638                          kobject_name(kobj), kobj);
 639                 kobject_del(kobj);
 640         }
 641 
 642         if (t && t->release) {
 643                 pr_debug("kobject: '%s' (%p): calling ktype release\n",
 644                          kobject_name(kobj), kobj);
 645                 t->release(kobj);
 646         }
 647 
 648         /* free name if we allocated it */
 649         if (name) {
 650                 pr_debug("kobject: '%s': free name\n", name);
 651                 kfree_const(name);
 652         }
 653 }

关键在645行,我们可以看见,最终的释放,调用的是我们注册进去的release函数。
attribute是一个属性,每一个属性在sysfs中对应一个文件,在该内核对象的目录下生成。在kobj_type内,attribute是一个二级指针,可以提供多个属性,一起注册。详细使用方法可以参考内核代码,有很多种范例。一定要谨记二级指针和数组指针的区别。这个我在编写测试用例的时候,没有参考内核代码是如何赋值的,结果犯了低级错误,导致了内核的崩溃。

209 struct sysfs_ops {                                                                                                                                                                                           
210         ssize_t (*show)(struct kobject *, struct attribute *, char *);
211         ssize_t (*store)(struct kobject *, struct attribute *, const char *, size_t);
212 };

操作方法实际上只有两个,一个show,一个store。上面介绍的属性只是一个单纯的属性,sysfs负责将属性和sysfs_ops联系起来,这涉及到文件系统的相关知识,这里不再赘述。只简单说一下过程:
attribute在sysfs下表现出是一个文件,打开该文件,将导致该文件的操作表和具体的sysfs_ops关联起来,对任意一个属性的读,将最终调用show,而写将调用store。写的内容或为读出内容而准备的缓冲区都被sysfs传递了下来,在函数里就是第三个参数。
但是这个属性文件是如何生成的呢?我们注意到229行的create_dir函数,将其展开:

  66 static int create_dir(struct kobject *kobj)
  67 {
  68         const struct kobj_ns_type_operations *ops;
  69         int error;
  70 
  71         error = sysfs_create_dir_ns(kobj, kobject_namespace(kobj));
  72         if (error)
  73                 return error;
  74 
  75         error = populate_dir(kobj);
  76         if (error) {
  77                 sysfs_remove_dir(kobj);
  78                 return error;
  79         }
  80 
  81         /*
  82          * @kobj->sd may be deleted by an ancestor going away.  Hold an
  83          * extra reference so that it stays until @kobj is gone.
  84          */
  85         sysfs_get(kobj->sd);
  86 
  87         /*
  88          * If @kobj has ns_ops, its children need to be filtered based on
  89          * their namespace tags.  Enable namespace support on @kobj->sd.
  90          */
  91         ops = kobj_child_ns_ops(kobj);
  92         if (ops) {
  93                 BUG_ON(ops->type <= KOBJ_NS_TYPE_NONE);
  94                 BUG_ON(ops->type >= KOBJ_NS_TYPES);
  95                 BUG_ON(!kobj_ns_type_registered(ops->type));
  96 
  97                 sysfs_enable_ns(kobj->sd);                                                                                                                                                                  
  98         }
  99 
 100         return 0;
 101 }
 102 

71行负责创建该obj对象对应的目录,这个函数最终会调用sysfs相关的文件系统接口创建目录,这里不再赘述。
75行负责创建属性对应的文件,我们将其展开:

  49 static int populate_dir(struct kobject *kobj)
  50 {
  51         struct kobj_type *t = get_ktype(kobj);
  52         struct attribute *attr;
  53         int error = 0;
  54         int i;
  55 
  56         if (t && t->default_attrs) {
  57                 for (i = 0; (attr = t->default_attrs[i]) != NULL; i++) {
  58                         error = sysfs_create_file(kobj, attr);
  59                         if (error)
  60                                 break;
  61                 }
  62         }
  63         return error; 
  64 }

56~62行,针对default_attr域进行检测以及遍历,对每一个attr创建文件,直到为NULL。因此,我们在对属性进行赋值的时候,最后一个属性一定需要赋值为NULL。虽然有时候系统会自动赋值为NULL,但是手动赋值为NULL更加安全。58行其实也是sysfs的创建文件的接口。具体实现略去不提。

分析到这里,其实又可以为自己的测试模块,添加一个真正的属性了。来验证一下,对属性的读写,是否真实的调用了show和store函数。

在测试之后,进入kobj的最后一个环节。我们之前提到过,kobj可以创建多个自定义的属性,对每个属性的操作方法也不同。然而,上面所说的,却是一对show和store函数统治所有属性。那么如何保证每个属性的都有自己的个性化操作呢?一个最简单的办法就是在函数内部调用swith函数判断是哪个属性下发的操作,然后进入自己的分支。但是这样的操作方式代码可读性,结构性不好。内核从来不使用这样的方法。下面我们介绍内核的使用方法。

下面是内核中一个模块实现的show方法:

 113 static ssize_t dev_attr_show(struct kobject *kobj, struct attribute *attr,
 114                              char *buf)
 115 {
 116         struct device_attribute *dev_attr = to_dev_attr(attr);
 117         struct device *dev = kobj_to_dev(kobj);
 118         ssize_t ret = -EIO;
 119 
 120         if (dev_attr->show)
 121                 ret = dev_attr->show(dev, dev_attr, buf);
 122         if (ret >= (ssize_t)PAGE_SIZE) {
 123                 print_symbol("dev_attr_show: %s returned bad count\n",
 124                                 (unsigned long)dev_attr->show);
 125         }
 126         return ret;
 127 }

116行,实际是一个container_of宏,我们看出来,实际上,内核会将struct attribute进行再封装,通过该宏取出个性化的属性,在121行调用自己的show方法。采用这种方法,便于添加属性也便于管理。下面贴出个性化的属性定义:

 548 struct device_attribute {
 549         struct attribute        attr;
 550         ssize_t (*show)(struct device *dev, struct device_attribute *attr,
 551                         char *buf);
 552         ssize_t (*store)(struct device *dev, struct device_attribute *attr,
 553                          const char *buf, size_t count);
 554 };

到这里,kobj相关的部分就分析完了,下面我们将进入kset的分析。当然,别忘了,按照内核里的个性化属性方法,编写自己的测试用例。

posted @ 2017-07-30 16:53  自然技术搬运工  阅读(995)  评论(0编辑  收藏  举报