读书笔记之linux内核设计与实现(16)
kobject和sysfs
 

        2.6内核增加了一个引人注目的新特性——统一设备模型(device model)。设备模型提供一个独立的机制专门来表示设备,并描述了其在系统中的拓扑结构。

17.1   kobject

       设备模型的核心部分就是kobject,它由struct kobject结构表示,定义于头文件(linux/kobject)中。kobject类似于C#或java这些面向对象语言中的object对象类,提供了诸如引用计数、名称和父指针等字段,可以创建对象的层次结构

struct kobject {
              char                          * k_name;
             char                           name[KOBJ_NAME_LEN];
               struct kref                  kref;
              struct list_head          entry;
              struct kobject              * parent;
              struct kset                  * kset;
              struct kobj_type         * ktype;
             struct dentry               * dentry;
};
k_name指针指向kobject名称的起始位置,如果名称长度小于KOBJ_NAME_LEN——当前为20个字节,那么该kobject的名称便存放到name数组中,k_name指向数组头;如果名称大于20,则动态分配以讹足够大的缓冲区来存放kobject的名称,这时k_name指向缓冲区。

       parent指针指向kobject的父对象。因此,kobject就会在内核中构造一个对象层次结构,并且可以将对各对象间的关系表现出来,就如你看到的,这便是sysfs的真正面目:一个用户空间的文件系统,用来表示内核中kobject对象的层次结构。

       dentry指针指向dentry结构体,在sysfs中该结构体就表示这个kobject,当然假设该kobject已反映在sysfs中。

        kobject通常是嵌入到其他结构中的,其单独意义其实并不大。相反,那些更为重要的结构体才真正需要用到kobject结构。比如struct   cdev。

struct    cdev   {

              struct     kobject               kobj;

              struct      module             *owner;

             struct     file_operations    *ops;

             struct     list_head           list;

             dev_t                             dev

              unsigned    int               count;

};

       当kobject被嵌入到其他结构体中时,该结构体便拥有了kobject提供的标准功能。更重要的一点是,嵌入kobject接结构体可以成为对象层次架构中的一部分。比如cdev结构体就可以通过其父进程指针cdev->kobj->parent和链表cdev->kobj->entry来插入到对象层次结构中

17.2   ktype

       kobject对象被关联到一种特殊的类型,即ktype。ktype由kobj_type结构体表示,定义于<linux/kobject.h>中:

struct kobj_type {
                  void                                 (*release)(struct kobject *);
                    struct     sysfs_ops          * sysfs_ops;
                    struct      attribute             ** default_attrs;
};
ktype是为了描述一族kobject所具有的普遍特性。因此,不再需要每个kobject都分别定义自己的特性,而是将这些普遍的特性在ktype结构体中一次定义,然后所有“同类”的kobject都能共享一样的特性。release指针指向在kobject引用计数减至0时要被调用的析构函数。该函数负责释放所有kobject使用的内存和其它相关清理工作。sysfs_ops变量指向sysfs_ops结构体。该结构体描述了sysfs文件读写时的特性default_attrs指向一个attribute结构体数组。这些结构体定义了该kobject相关的默认属性。属性给定了对象的特征,如果该kobject被导出到sysfs中,那么这些属性都将相应的作为文件而导出。

17.3   kset

       kset是kobject对象的集合体。把它看成一个容器,可将所有相关的kobject对象,比如“全部的块设备”置于同一位置。kset可把kobject集中到一个集合中,而ktype描述相关类型kobject所共有的特性,它们之间的重要区别在于:具有相同ktype的kobject可以分到不同的kset中(同一个kset中的kobject是否具有相同的ktype吗???)。kobject的kset指针指向相应的kset集合。

struct kset {
          struct    subsystem              * subsys;
          struct     kobj_type                * ktype;
          struct     list_head                 list;
          struct   kobject                    kobj;
         struct    kset_hotplug_ops       * hotplug_ops;
};
        其中ktype指针指向集合(kset)中的kobject对象的类型(ktype),list连接该集合(kset)中所有的kobject对象。kobj指向的kobject对象代表了该集合的基类。hotplug_ops指向一个用于处理集合中kobject对象的热拔插操作的结构体。subsys指针指向该结构体相关的struct subsystem结构体。

17.4   subsystem

       subsystem在内核中代表高层概念,它是一个或多个kset的大集合。

struct subsystem {
struct kset   kset;
struct rw_semaphore rwsem;
};
        虽然subsystem结构体只指向一个kset,但是多个kset可以通过其subsys指针指向一个subsystem这种单向关系意味着不可能仅仅通过一个subsystem结构体就找到所有的ksets。subsystem中的kset字段指向的是subsystem中的默认kset,它在对象层次结构中起到了粘合剂的作用。rwsem是一个读写信号量,它用来对subsystem和它的所有kset进行并发访问保护。

17.6    管理和操作kobject

17.7    引用计数

kobject的主要功能之一就是为我们提供了一个统一的引用计数系统。初始化后,kobject的引用计数设置为1。只要引用计数不为0,那么该对象就会继续保留在内存中,也可以说是被“钉”住了,任何包含对象引用的代码首先要增加该对象的引用计数,当代码结束后则减少它的引用计数。当引用计数减为0时,对象便可以被销毁,同时相关内存也都被释放。

       增加一个引用计数可通过kobject_get()函数完成

struct   kobject    * kobject_get(struct kobject   *kobj);

该函数正常情况下返回一个指向kobject的指针,如果失败,则返回NULL指针。

减少引用计数通过kobject_put()完成

void   kobject_put(struct kobject    *kobj);

17.8    sysfs

       sysfs文件系统是一个处于内存中的虚拟文件系统,它为我们提供了kobject对象层次结构的视图。帮助用户可以一个简单文件系统的方式来观察系统中各种设备的拓扑结构。借助属性对象,kobject可用导出文件的方式,将内核变量提供给用户读取或写入。

      sysfs的诀窍是把kobject对象与目录项紧密联系起来,这点是通过kobject中的dentry(directory entry)字段实现的。回忆一下12章,dentry结构体表示目录项,通过连接kobject到指定的目录项上,无疑方便的将kobject映射到该目录上。好了,kobject其实已经形成了一棵树了——就是我们心爱的对象模型体系。

       sysfs的根目录下包含了七个子目录:block、bus、class、devices、firmware、module和power。block目录下的每个子目录都对应着系统中的一个块设备。反过来,每个目录下又都包含了该块设备的所有分区。bus目录提供了一个系统总线视图。class目录包含了以高层功能逻辑组织起来的系统设备视图。devices目录是系统中设备拓扑结构视图,它直接映射出了内核中设备结构体的组织层次。firmware目录包含一些诸如ACPI、EDD、EFI等低层子系统的特殊树。power目录包含了系统范围的电源管理数据。

17.8.1   sysfs中添加和删除kobject

       仅仅初始化一个kobject是不能自动将其导出到sysfs中的,想要把kobject导入sysfs,需要用到函数kobject_add():

int   kobject_add(struct   kobject   *kobj);

       kobject在sysfs中的位置取决于kobject在对象层次结构中的位置。如果kobject的父指针被设置,那么在sysfs中kobject将被映射为其父目录下的子目录,如果parent没有设置,那么kobject将被映射到kset->kobj中的子目录。两者都未设置,映射为sysfs下的根级目录。

17.8.2   向sysfs中添加文件

       我们已经看到kobject被映射为文件目录,而且所有的对象层次结构都优雅的、一个不少的映射成为sys下的目录结构。但是里面的文件是什么?sysfs仅仅是一个漂亮的树,但是没有提供实际数据的文件。

       默认属性

       默认的文件集合是通过kobject和kset中的ktype字段提供的。因此所有具有相同类型的kobject在它们对应的sysfs目录下都拥有相同的默认文件集合。kobj_type字段含有一个字段——default_attrs,它是看一个attribute结构体数组。这些属性负责将内核数据映射成sysfs中的文件。

struct attribute {
            char                         * name;
             struct    module       * owner;
           mode_t                     mode;
};
其中name字段提供了该属性的名称,最终出现在sysfs中的文件名就是它。owner字段在存在所属模块的情况下指向其所属的module结构体。如果一个模块没有该属性,那么该字段为NULL。mode字段类型为mode_t,他表示sysfs中该文件的权限。

       虽然default_attrs列出了默认的属性,sysfs_ops字段则描述了如何使用它们。sysfs_ops字段指向了一个定义与文件<linux/sysfs.h>的同名结构体:

struct sysfs_ops {
ssize_t (*show)(struct kobject *,     struct attribute *,       char *);
ssize_t (*store)(struct kobject *,     struct attribute *,       const char *, size_t);
};
      show()方法在读操作时被调用。它会拷贝由attr提供的属性值到buffer指定的缓冲区,缓冲区大小为PAGE_SIZE字节;该函数若执行成功,返回写入buffer的字节数,失败,返回负的错误码。

     store()方法在写操作时调用,它会从buffer中读取size大小的字节,并将其放入attr表示的属性结构体变量中