Chinaunix首页 | 论坛 | 博客
  • 博客访问: 204935
  • 博文数量: 70
  • 博客积分: 0
  • 博客等级: 民兵
  • 技术积分: 412
  • 用 户 组: 普通用户
  • 注册时间: 2013-08-30 11:07
文章分类

全部博文(70)

文章存档

2014年(68)

2013年(2)

我的朋友

分类: LINUX

2014-05-14 10:27:35

sysfs虚拟文件系统:

与设备模型密切相关,其中显示的每个对象,都对应一个kobjectkobject用来与内核交互并创建可见表述。sysfs向外界展示了其所表述的结构,向用户空间提供系统信息,以及改变操作参数的结构。

sysfs 文件系统具有树型结构, 反映 kobject之间的组织层次关系。为了表示驱动程序和所管理的设备间的关系,需要额外的指针,其在 sysfs 中通过符号链接实现。

/* sysfs 创建一个符号链接:*/

int sysfs_create_link(struct kobject *kobj, struct kobject *target, char *name);

/*函数创建一个链接(name)指向target sysfs 入口作为 kobj 的一个属性,是一个相对连接,与它在sysfs 系统中的位置无关*/

/*删除符号连接调用:*/

void sysfs_remove_link(struct kobject *kobj, char *name);

 

子系统:

通常显示在sysfs分层结构中的顶层。

内核中的子系统:block_subsys(对块设备来说是/sys/block);

                                     devices_subsys/sys/devices,设备分层结构的核心)

                                     内核所知晓的用于各种总线的特定子系统

简单结构

Struct subsystem{

         Struct kset kset;               /*每一个kset都属于一个子系统,subsystemlinux kernel 在分层结构中定位kset*/

         Struct rw_semaphore rwsem;   //串行方位kset内部链表

}

decl_subsys(name, struct kobj_type *type, struct kset_hotplug_ops *hotplug_ops);

void subsysstem_init(struct subsystem *subsys);

int subsystem_register(struct subsystem *subsys);

void subsystem_unregister(struct subsystem *subsys);

struct subsystem *subsys_get(struct subsystem *subsys);

void subsys_put(struct subsystem *subsys);

大多数函数都是对子系统的kset操作,通过kset结构终端subsys指针可以找到每一个子系统,但无法从subsystem结构中找到子系统所包含的多个kset

 

Kobject 

组成设备的基本结构,意义在于把高级设备连接到设备模型上。如果以面向对象的方法思考,kobject为基类,其它类为它的派生产物。

         初始它只被作为一个简单的引用计数, 但随时间的推移,其任务越来越多。现在kobject 所处理的任务和支持代码包括:

1.对象的引用计数 :跟踪对象生命周期的一种方法是使用引用计数。当没有内核代码持有该对象的引用时, 该对象将结束自己的有效生命期并可被删除。

2.sysfs 表述:在 sysfs 中出现的每个对象(即目录)都对应一个 kobject, 它和内核交互来创建它的可见表述。每个kobject都输出一个或多个属性,它在kobjectsysfs目录中以文件的形式出现,其中的内容由内核产生。包含sysfs 的工作代码。

3.数据结构关联:整体来看, 设备模型是一个极端复杂的数据结构,通过其间的大量链接而构成一个多层次的体系结构。kobject 实现了该结构并将其聚合在一起。

4.热插拔事件处理 :kobject 子系统将产生的热插拔事件通知用户空间。

 

结构如下:

struct kobject {

         char                    * k_name;        /*kobject 的名字数组(sysfs 入口使用的名字)指针;如果名字数组大小小于KOBJ_NAME_LEN,它指向本数组的name,否则指向另外分配的一个名字数组空间 */

         char                    name[KOBJ_NAME_LEN];        /*kobject 的名字数组,若名字数组大小不小于KOBJ_NAME_LEN,只储存前KOBJ_NAME_LEN个字符*/

         struct kref                  kref;          //kobject 的引用计数

         struct list_head        entry;       //kobject 之间的双向链表,与所属的kset形成环形链表

         struct kobject           * parent;           /*sysfs分层结构中定位对象,指向上一级kset中的struct kobject kobj*/

         struct kset                 * kset;               //指向所属的kset

         struct kobj_type       * ktype;             /*负责对该kobject类型进行跟踪的struct kobj_type的指针*/

         struct dentry             * dentry;           /*sysfs文件系统中与该对象对应的文件节点路径指针*/

         wait_queue_head_t    poll;          /*等待队列头*/

};

struct kobj_type {

         void (*release)(struct kobject *);

         struct sysfs_ops       * sysfs_ops;              //提供实现以下属性的方法

         struct attribute        ** default_attrs;     //用于保存类型属性列表(指针的指针)

};

 

某些结构中(如cdev)嵌入了kobject结构,可以用container of 宏通过kobject指针回找嵌入的原结构体的指针。

 

当创建kobject , 每个 kobject 都被给定一系列默认属性。这些属性保存在kobj_type 结构中:

struct attribute {

 char *name;           /*属性的名字( kobject sysfs 目录中显示)*/

 struct module *owner;           /*指向模块的指针(如果有), 此模块负责实现这个属性*/

 mode_t mode;               /*属性的保护位,modes 的宏定义在 :例如S_IRUGO 为只读属性等等*/

};                /*default_attrs 列表中的最后一个元素必须用 0 填充*/

 sysfs 读写这些属性是由 kobj_type->sysfs_ops 成员中的函数完成的:

 

struct sysfs_ops {

 ssize_t (*show)(struct kobject *kobj, struct attribute *attr, char *buffer);

 ssize_t (*store)(struct kobject *kobj, struct attribute *attr, const char *buffer, size_t size);

};

 

当用户空间读取一个属性时,内核会使用指向 kobject 的指针(kobj)和正确的属性结构(*attr)来调用show 方法,该方法将给定属性值编码进缓冲(buffer)(注意不要越界( PAGE_SIZE 字节)), 并返回实际数据长度。sysfs 的约定要求每个属性应当包含一个单个人眼可读值; 若返回大量信息,需将它分为多个属性.

 

也可对所有 kobject 关联的属性使用同一个 show 方法,用传递到函数的 attr 指针来判断所请求的属性。有的 show 方法包含对属性名字的检查。有的show 方法会将属性结构嵌入另一个结构, 这个结构包含需要返回属性值的信息,这时可用container_of 获得上层结构的指针以返回属性值的信息。

 

store 方法将存在缓冲(buffer)的数据( size 为数据的长度,不能超过 PAGE_SIZE )解码并保存新值到属性(*attr, 返回实际解码的字节数。store 方法只在拥有属性的写权限时才能被调用。此时注意:接收来自用户空间的数据一定要验证其合法性。如果到数据不匹配, 返回一个负的错误值。

 

sysfs中:

虽然 kobject 类型的 default_attrs 成员描述了所有的 kobject 会拥有的属性,倘若想添加新属性(非默认属性)到 kobject sysfs 目录属性只需简单地填充一个attribute结构并传递到以下函数:

int sysfs_create_file(struct kobject *kobj, struct attribute *attr);

/*若成功,文件以attribute结构中的名字创建并返回 0; 否则, 返回负错误码*/

/*注意:内核会调用相同的 show() store() 函数来实现对新属性的操作,所以在添加一个新非默认属性前,应采取必要的步骤确保这些函数知道如何实现这个属性*/

若要删除属性,调用:

int sysfs_remove_file(struct kobject *kobj, struct attribute *attr);

/*调用后, 这个属性不再出现在 kobject sysfs 入口。若一个用户空间进程可能有一个打开的那个属性的文件描述符,在这个属性已经被删除后,show store 仍然可能被调用*/

 

sysfs通常要求所有属性都只包含一个可读文本格式的值,很少需要创建能够处理大量二进制数据的属性。但当在用户空间和设备间传递不可改变的数据时(如上传固件到设备)就需要这个特性。二进制属性使用一个 bin_attribute 结构来描述:

struct bin_attribute {

    struct attribute    attr;/*属性结构体*/

    size_t            size;/*这个二进制属性的最大大小(若无最大值则为0)*/

    void            *private;

    ssize_t (*read)(struct kobject *, char *, loff_t, size_t);

    ssize_t (*write)(struct kobject *, char *, loff_t, size_t);

/*read write 方法类似字符驱动的读写方法;,在一次加载中可被多次调用,每次调用最大操作一页数据,且必须能以其他方式判断操作数据的末尾*/

    int (*mmap)(struct kobject *, struct bin_attribute *attr,

         struct vm_area_struct *vma);

};

/*二进制属性必须显式创建,不能以默认属性被创建,创建一个二进制属性调用:*/

int sysfs_create_bin_file(struct kobject *kobj, struct bin_attribute *attr);

/*删除二进制属性调用:*/

int sysfs_remove_bin_file(struct kobject *kobj, struct bin_attribute *attr);

 

kobject 初始化

kobject的初始化较为复杂,但是必须的步骤如下:

1)将整个kobject清零,通常使用memset函数。

2)调用kobject_init()函数,设置结构内部一些成员。所做的一件事情是设置kobject的引用计数为1。具体的源码如下:

void kobject_init(struct kobject * kobj)/*in kobject.c*/

{

    if (!kobj)

        return;

    kref_init(&kobj->kref);/*设置引用计数为1*/

    INIT_LIST_HEAD(&kobj->entry);/*初始化kobject 之间的双向链表*/

    init_waitqueue_head(&kobj->poll);/*初始化等待队列头*/

    kobj->kset = kset_get(kobj->kset);/*增加所属kset的引用计数(若没有所属的kset,则返回NULL*/

}

void kref_init(struct kref *kref)/*in kobject.c*/

{

    atomic_set(&kref->refcount,1);

    smp_mb();

}

static inline struct kset * to_kset(struct kobject * kobj)/*in kobject.h*/

{

    return kobj ? container_of(kobj,struct kset,kobj) : NULL;

}

static inline struct kset * kset_get(struct kset * k)/*in kobject.h*/

{

    return k ? to_kset(kobject_get(&k->kobj)) : NULL;/*增加引用计数*/

}

3)设置kobject的名字

 

int kobject_set_name(struct kobject * kobj, const char * fmt, ...);

4)直接或间接设置其它成员:ktypeksetparent。 (重要)

 

应用:

1kobject 的一个重要函数是为包含它的结构设置引用计数。只要对这个对象的引用计数存在, 这个对象( 和支持它的代码) 必须继续存在。底层控制 kobject 的引用计数的函数有:

 

struct kobject *kobject_get(struct kobject *kobj);

/*若成功,递增kobject的引用计数并返回一个指向kobject的指针,否则返回NULL。必须始终测试返回值以免产生竞态*/

void kobject_put(struct kobject *kobj);

/*递减引用计数并在可能的情况下释放这个对象*/

注意:kobject _init 设置这个引用计数为 1,因此创建一个 kobject, 当这个初始化引用不再需要,应当确保采取 kobject_put 调用。

引用计数不由创建 kobject 的代码直接控制,当 kobject 的最后引用计数消失时,必须异步通知,而后kobjectktype所指向的kobj_type结构体包含的release函数会被调用。通常原型如下:

 

void my_object_release(struct kobject *kobj)

{

 struct my_object *mine = container_of(kobj, struct my_object, kobj);

/* Perform any additional cleanup on this object, then... */

 kfree(mine);

}

每个 kobject 必须有一个release函数, 并且这个 kobject 必须在release函数被调用前保持不变( 稳定状态 ) 。这样,每一个 kobject 需要有一个关联的 kobj_type 结构,指向这个结构的指针能在 2 个不同的地方找到:

1kobject 结构自身包含一个成员(ktype)指向kobj_type

2)如果这个 kobject 是一个 kset 的成员, kset 会提供kobj_type 指针。

以下宏用以查找指定kobjectkobj_type 指针:

struct kobj_type *get_ktype(struct kobject *kobj);

这个函数其实就是从以上提到的这两个地方返回kobj_type指针,源码如下:

static inline struct kobj_type * get_ktype(struct kobject * k)

{

    if (k->kset && k->kset->ktype)

        return k->kset->ktype;

    else

        return k->ktype;

}

 

举例:struct cdev 的引用计数实现如下:

struct kobject *cdev_get(struct cdev *p)

{

 struct module *owner = p->owner;

 struct kobject *kobj;

 if (owner && !try_module_get(owner))

 return NULL;

 kobj = kobject_get(&p->kobj);

 if (!kobj)

 module_put(owner);

 return kobj;

}

创建一个对 cdev 结构的引用时,还需要创建包含它的模块的引用。因此, cdev_get 使用 try_module_get 来试图递增这个模块的使引用计数。如果这个操作成功, kobject_get 被同样用来递增 kobject 的引用计数。kobject_get 可能失败, 因此这个代码检查 kobject_get 的返回值,如果调用失败,则释放它的对模块的引用计数。

 

Kset

kset kobj_type 结构的扩展; 一个 kset 是嵌入到相同类型结构的 kobject 的集合。但 struct kobj_type 关注的是对象的类型,而struct kset 关心的是对象的聚合和集合,其主要功能是包容,可认为是kobjects 的顶层容器类。每个 kset 在内部包含自己的 kobject, 并可以用多种处理kobject 的方法处理kset ksets 总是在 sysfs 中出现; 一旦设置了 kset 并把它添加到系统中, 将在 sysfs 中创建一个目录;kobjects 不必在 sysfs 中表示, kset中的每一个 kobject 成员都在sysfs中得到表述。

sysfs中创建kobject的入口是kobject_add的工作的一部分,只要调用kobject_add 就会在sysfs中显示,还有些知识值得记住:

1kobjectssysfs入口始终为目录,kobject_add的调用将在sysfs中创建一个目录,这个目录包含一个或多个属性(文件);

2)分配给kobject的名字( kobject_set_name ) sysfs中的目录名,出现在 sysfs层次的相同部分的kobjects必须有唯一的名字。分配给kobjects的名字也应当是合法的文件名字:它们不能包含非法字符(如:斜线)且不推荐使用空白。

3sysfs入口位置对应 kobject parent 指针。若parentNULL,则它被设置为嵌入到新kobjectkset中的kobject;若parentkset都是NULL,则sysfs入口目录在顶层,通常不推荐。

struct kset {

    struct kobj_type    * ktype; /*指向该kset对象类型的指针*/

    struct list_head    list;/*用于连接该kset中所有kobject以形成环形链表的链表头*/

    spinlock_t        list_lock;/*用于避免竞态的自旋锁*/

    struct kobject        kobj; /*嵌入的kobject*/

    struct kset_uevent_ops    * uevent_ops;

/*原有的struct kset_hotplug_ops * hotplug_ops;已经不存在,被kset_uevent_ops 结构体替换,在热插拔操作中会介绍*/

};

 

增加 kobject kset 中去,通常是在kobject 创建时完成,其过程分为2步:

1)完成kobject的初始化,特别注意maneparent和初始化。

2)把kobject kset 成员指向目标kset

3)将kobject 传递给下面的函数:

int kobject_add(struct kobject *kobj); /*函数可能失败(返回一个负错误码),程序应作出相应地反应*/

内核提供了一个组合函数:

extern int kobject_register(struct kobject *kobj); /*仅仅是一个 kobject_init kobject_add 的结合,其他成员的初始化必须在之前手动完成*/

 

当把一个kobjectkset中删除以清除引用时使用:

void kobject_del(struct kobject *kobj);/* kobject_del kobject_put 的结合*/

 

kset 在一个标准的内核链表中保存了它的子节点,在大部分情况下, 被包含的 kobjects 在它们的 parent 成员中保存指向 kset内嵌的 kobject的指针,关系如下:

图表中的所有的被包含的 kobjects 实际上被嵌入在一些其他类型中, 甚至可能其他的 kset

 

Kset上的操作

ksets 有类似于kobjects初始化和设置接口:

void kset_init(struct kset *kset);

int kset_add(struct kset *kset);

int kset_register(struct kset *kset);

void kset_unregister(struct kset *kset);

 

/*管理 ksets 的引用计数:*/

struct kset *kset_get(struct kset *kset);

void kset_put(struct kset *kset);

 

/* kset 也有一个名字,存储于嵌入的 kobject,因此设置它的名字用:*/

kobject_set_name(&my_set->kobj, "The name");

ksets 还有一个指针指向 kobj_type 结构来描述它包含的 kobject,这个类型优先于 kobject 自身中的 ktype 。因此在典型的应用中, struct kobject 中的 ktype 成员被设为 NULL, kset 中的ktype是实际被使用的。

在新的内核里, kset 不再包含一个子系统指针struct subsystem * subsys, 而且subsystem已经被kset取代。

阅读(1294) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~